You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by da...@apache.org on 2011/07/28 23:59:01 UTC

svn commit: r1152016 [5/5] - in /subversion/branches/revprop-packing: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ notes/ subversion/bindings/swig/ subversion/bindings/swig/perl/native/t/ subversion/include/ subversion/include/priv...

Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/externals_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/externals_tests.py?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/externals_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/externals_tests.py Thu Jul 28 21:58:49 2011
@@ -28,7 +28,6 @@
 import sys
 import os
 import re
-import tempfile
 
 # Our testing module
 import svntest
@@ -214,25 +213,18 @@ def externals_test_setup(sbox):
 def change_external(path, new_val, commit=True):
   """Change the value of the externals property on PATH to NEW_VAL,
   and commit the change unless COMMIT is False."""
-  (fd, tmp_f) = tempfile.mkstemp(dir=svntest.main.temp_dir)
-  svntest.main.file_append(tmp_f, new_val)
-  svntest.actions.run_and_verify_svn(None, None, [], 'pset',
-                                     '-F', tmp_f, 'svn:externals', path)
+
+  svntest.actions.set_prop('svn:externals', new_val, path)
   if commit:
     svntest.actions.run_and_verify_svn(None, None, [], 'ci',
                                        '-m', 'log msg', '--quiet', path)
-  os.close(fd)
-  os.remove(tmp_f)
 
 def change_external_expect_error(path, new_val, expected_err):
   """Try to change the value of the externals property on PATH to NEW_VAL,
   but expect to get an error message that matches EXPECTED_ERR."""
-  (fd, tmp_f) = tempfile.mkstemp(dir=svntest.main.temp_dir)
-  svntest.main.file_append(tmp_f, new_val)
-  svntest.actions.run_and_verify_svn(None, None, expected_err, 'pset',
-                                     '-F', tmp_f, 'svn:externals', path)
-  os.close(fd)
-  os.remove(tmp_f)
+
+  svntest.actions.set_prop('svn:externals', new_val, path,
+                           expected_err=expected_err)
 
 
 def probe_paths_exist(paths):

Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout Thu Jul 28 21:58:49 2011
@@ -87,8 +87,9 @@ Global options:
   --password ARG           : specify a password ARG
   --no-auth-cache          : do not cache authentication tokens
   --non-interactive        : do no interactive prompting
-  --trust-server-cert      : accept unknown SSL server certificates without
-                             prompting (but only with '--non-interactive')
+  --trust-server-cert      : accept SSL server certificates from unknown
+                             certificate authorities without prompting (but only
+                             with '--non-interactive')
   --config-dir ARG         : read user configuration files from directory ARG
   --config-option ARG      : set user configuration option in the format:
                                  FILE:SECTION:OPTION=[VALUE]
@@ -165,8 +166,9 @@ Global options:
   --password ARG           : specify a password ARG
   --no-auth-cache          : do not cache authentication tokens
   --non-interactive        : do no interactive prompting
-  --trust-server-cert      : accept unknown SSL server certificates without
-                             prompting (but only with '--non-interactive')
+  --trust-server-cert      : accept SSL server certificates from unknown
+                             certificate authorities without prompting (but only
+                             with '--non-interactive')
   --config-dir ARG         : read user configuration files from directory ARG
   --config-option ARG      : set user configuration option in the format:
                                  FILE:SECTION:OPTION=[VALUE]

Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/info_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/info_tests.py?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/info_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/info_tests.py Thu Jul 28 21:58:49 2011
@@ -449,8 +449,8 @@ def info_show_exclude(sbox):
 
   expected_info = [{
       'Path' : '.*%siota' % re.escape(os.sep),
-     'Repository Root' : sbox.repo_url,
-     'Repository UUID' : wc_uuid,
+      'Repository Root' : sbox.repo_url,
+      'Repository UUID' : wc_uuid,
   }]
   svntest.main.run_svn(None, 'up', '--set-depth', 'exclude', iota)
   svntest.actions.run_and_verify_info(expected_info, iota)
@@ -469,19 +469,16 @@ def info_show_exclude(sbox):
 
   sbox.simple_rm('iota')
   sbox.simple_commit()
+  
+  expected_error = 'svn: E200009: Could not display info for all targets.*'
 
   # Expect error on iota (status = not-present)
-  svntest.actions.run_and_verify_svn(None, [],
-       'svn: E200009: Could not display info for all targets.*',
-        'info', iota)
+  svntest.actions.run_and_verify_svn(None, [], expected_error, 'info', iota)
 
   sbox.simple_update()
 
   # Expect error on iota (unversioned)
-  svntest.actions.run_and_verify_svn(None, [],
-       'svn: E200009: Could not display info for all targets.*',
-        'info', iota)
-
+  svntest.actions.run_and_verify_svn(None, [], expected_error, 'info', iota)
 
 ########################################################################
 # Run the tests

Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/lock_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/lock_tests.py?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/lock_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/lock_tests.py Thu Jul 28 21:58:49 2011
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+# encoding=utf-8
 #
 #  lock_tests.py:  testing versioned properties
 #
@@ -1720,6 +1721,27 @@ def block_unlock_if_pre_unlock_hook_fail
                                       1, 'unlock', pi_path)
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
+#----------------------------------------------------------------------
+def lock_invalid_token(sbox):
+  "verify pre-lock hook returning invalid token"
+
+  sbox.build()
+
+  hook_path = os.path.join(sbox.repo_dir, 'hooks', 'pre-lock')
+  svntest.main.create_python_hook_script(hook_path,
+    '# encoding=utf-8\n'
+    'import sys\n'
+    'sys.stdout.write("тест")\n'
+    'sys.exit(0)\n')
+
+  fname = 'iota'
+  file_path = os.path.join(sbox.wc_dir, fname)
+
+  svntest.actions.run_and_verify_svn2(None, None,
+                                      "svn: warning: W160037: " \
+                                      ".*scheme.*'opaquelocktoken'", 0,
+                                      'lock', '-m', '', file_path)
+
 
 ########################################################################
 # Run the tests
@@ -1768,6 +1790,7 @@ test_list = [ None,
               cp_isnt_ro,
               update_locked_deleted,
               block_unlock_if_pre_unlock_hook_fails,
+              lock_invalid_token,
             ]
 
 if __name__ == '__main__':

Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/log_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/log_tests.py?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/log_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/log_tests.py Thu Jul 28 21:58:49 2011
@@ -1975,7 +1975,6 @@ def merge_sensitive_log_ignores_cyclic_m
 
 #----------------------------------------------------------------------
 @Issue(3931,3936)
-@XFail(svntest.main.is_ra_type_dav_serf)
 def log_with_unrelated_peg_and_operative_revs(sbox):
   "log with unrelated peg and operative rev targets"
 
@@ -1983,12 +1982,8 @@ def log_with_unrelated_peg_and_operative
 
   target = sbox.repo_url + '/A/D/G/rho@2'
 
-  # Currently this test fails because ra_serf returns an SVN_ERR_FS_NOT_FOUND
-  # error from svn_ra_get_locations() that the other RA layers do not
-  # return. The test passes with all other RA layers. See issue #3936.
-
   # log for /A/D/G/rho, deleted in revision 5, recreated in revision 8
-  expected_error = ".*File not found.*"
+  expected_error = ".*(File|path) not found.*"
   svntest.actions.run_and_verify_svn(None, None, expected_error,
                                      'log', '-r', '6:7', target)
   svntest.actions.run_and_verify_svn(None, None, expected_error,
@@ -2008,7 +2003,6 @@ def log_with_unrelated_peg_and_operative
 
 #----------------------------------------------------------------------
 @Issue(3937)
-@XFail(svntest.main.is_ra_type_dav_serf)
 def log_on_nonexistent_path_and_valid_rev(sbox):
   "log on nonexistent path does not error out"
 
@@ -2028,8 +2022,6 @@ def log_on_nonexistent_path_and_valid_re
   svntest.actions.run_and_verify_svn(None, None, expected_error,
                                      'log', '-q', bad_url_bad_rev)
 
-  # Currently this test fails over ra_serf because the following log
-  # commands return empty logs rather than errors.
   expected_error = ".*not found.*"
   svntest.actions.run_and_verify_svn(None, None, expected_error,
                                      'log', '-q', bad_path_real_rev)

Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/special_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/special_tests.py?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/special_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/special_tests.py Thu Jul 28 21:58:49 2011
@@ -711,6 +711,7 @@ def unrelated_changed_special_status(sbo
                                      '-m', 'psi changed special status')
 
 
+@Issue(3972)
 @SkipUnless(svntest.main.is_posix_os)
 def symlink_destination_change(sbox):
   "revert a symlink destination change"
@@ -747,6 +748,10 @@ def symlink_destination_change(sbox):
   expected_status.tweak('newfile', status='  ')
   svntest.actions.run_and_verify_status(wc_dir, expected_status)
 
+  # Issue 3972, repeat revert produces no output
+  svntest.actions.run_and_verify_svn(None, [], [], 'revert', '-R', wc_dir)
+  svntest.actions.run_and_verify_status(wc_dir, expected_status)
+
   # Now replace the symlink with a normal file and try to commit, we
 
 #----------------------------------------------------------------------

Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/svnadmin_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/svnadmin_tests.py?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/svnadmin_tests.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/svnadmin_tests.py Thu Jul 28 21:58:49 2011
@@ -26,6 +26,7 @@
 
 # General modules
 import os
+import re
 import shutil
 import sys
 
@@ -33,6 +34,7 @@ import sys
 import svntest
 from svntest.verify import SVNExpectedStdout, SVNExpectedStderr
 from svntest.verify import SVNUnexpectedStderr
+from svntest.verify import UnorderedOutput
 from svntest.main import SVN_PROP_MERGEINFO
 
 # (abbreviation)
@@ -1381,6 +1383,86 @@ def verify_non_utf8_paths(sbox):
     'STDERR', expected_stderr, errput):
     raise svntest.Failure
 
+def test_lslocks_and_rmlocks(sbox):
+  "test 'svnadmin lslocks' and 'svnadmin rmlocks'"
+  
+  sbox.build(create_wc=False)
+  iota_url = sbox.repo_url + '/iota'
+  lambda_url = sbox.repo_url + '/A/B/lambda'
+
+  exit_code, output, errput = svntest.main.run_svnadmin("lslocks",
+                                                        sbox.repo_dir)
+
+  if exit_code or errput or output:
+    raise svntest.Failure("Error: 'lslocks' failed")
+
+  expected_output = UnorderedOutput(
+    ["'A/B/lambda' locked by user 'jrandom'.\n",
+     "'iota' locked by user 'jrandom'.\n"])
+  
+  # Lock iota and A/B/lambda using svn client
+  svntest.actions.run_and_verify_svn(None, expected_output,
+                                     [], "lock", "-m", "Locking files",
+                                     iota_url, lambda_url)
+
+  expected_output = svntest.verify.UnorderedRegexOutput([
+      "Path: /A/B/lambda",
+      "UUID Token: opaquelocktoken",
+      "Owner: jrandom",
+      "Created:",
+      "Expires:",
+      "Comment \(1 line\):",
+      "Locking files",
+      "Path: /iota",
+      "UUID Token: opaquelocktoken.*",      
+      "\n", # empty line    
+      ])
+
+  # List all locks
+  exit_code, output, errput = svntest.main.run_svnadmin("lslocks",
+                                                        sbox.repo_dir)
+  
+  if errput:
+    raise SVNUnexpectedStderr(errput)
+    
+  svntest.verify.compare_and_display_lines('message', 'label',
+                                           expected_output, output)
+  svntest.verify.verify_exit_code(None, exit_code, 0)
+
+  # List lock in path /A
+  exit_code, output, errput = svntest.main.run_svnadmin("lslocks",
+                                                        sbox.repo_dir,
+                                                        "A")
+  if errput:
+    raise SVNUnexpectedStderr(errput)
+
+  expected_output = svntest.verify.UnorderedRegexOutput([
+    "Path: /A/B/lambda",
+    "UUID Token: opaquelocktoken",
+    "Owner: jrandom",
+    "Created:",
+    "Expires:",
+    "Comment \(1 line\):",
+    "Locking files",
+    "\n", # empty line    
+    ])
+
+  svntest.verify.compare_and_display_lines('message', 'label',
+                                           expected_output, output)
+  svntest.verify.verify_exit_code(None, exit_code, 0)
+
+  # Remove locks
+  exit_code, output, errput = svntest.main.run_svnadmin("rmlocks",
+                                                        sbox.repo_dir,
+                                                        "iota",
+                                                        "A/B/lambda")
+  expected_output = UnorderedOutput(["Removed lock on '/iota'.\n",
+                                     "Removed lock on '/A/B/lambda'.\n"])
+  
+  svntest.verify.verify_outputs(
+    "Unexpected output while running 'svnadmin rmlocks'.",
+    output, [], expected_output, None)
+
 ########################################################################
 # Run the tests
 
@@ -1410,6 +1492,7 @@ test_list = [ None,
               hotcopy_symlink,
               load_bad_props,
               verify_non_utf8_paths,
+              test_lslocks_and_rmlocks,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/revprop-packing/subversion/tests/cmdline/svntest/actions.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/cmdline/svntest/actions.py?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/cmdline/svntest/actions.py (original)
+++ subversion/branches/revprop-packing/subversion/tests/cmdline/svntest/actions.py Thu Jul 28 21:58:49 2011
@@ -1822,12 +1822,12 @@ def set_prop(name, value, path, expected
   if value and (value[0] == '-' or '\x00' in value or sys.platform == 'win32'):
     from tempfile import mkstemp
     (fd, value_file_path) = mkstemp()
+    os.close(fd)
     value_file = open(value_file_path, 'wb')
     value_file.write(value)
     value_file.flush()
     value_file.close()
     main.run_svn(expected_err, 'propset', '-F', value_file_path, name, path)
-    os.close(fd)
     os.remove(value_file_path)
   else:
     main.run_svn(expected_err, 'propset', name, value, path)

Modified: subversion/branches/revprop-packing/subversion/tests/libsvn_client/client-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/libsvn_client/client-test.c?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/libsvn_client/client-test.c (original)
+++ subversion/branches/revprop-packing/subversion/tests/libsvn_client/client-test.c Thu Jul 28 21:58:49 2011
@@ -244,6 +244,7 @@ check_patch_result(const char *path, con
   svn_pool_destroy(iterpool);
 
   SVN_TEST_ASSERT(i == num_expected_lines);
+  SVN_ERR(svn_stream_close(stream));
   SVN_ERR(svn_io_remove_file2(path, FALSE, pool));
 
   return SVN_NO_ERROR;

Modified: subversion/branches/revprop-packing/subversion/tests/libsvn_diff/parse-diff-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/libsvn_diff/parse-diff-test.c?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/libsvn_diff/parse-diff-test.c (original)
+++ subversion/branches/revprop-packing/subversion/tests/libsvn_diff/parse-diff-test.c Thu Jul 28 21:58:49 2011
@@ -247,28 +247,28 @@ static const char *bad_git_diff_header =
   "new file mode 100644"                                                NL;
 
 
-/* Create a PATCH_FILE with name FNAME containing the contents of DIFF. */
+/* Create a PATCH_FILE containing the contents of DIFF. */
 static svn_error_t *
-create_patch_file(svn_patch_file_t **patch_file, const char *fname,
+create_patch_file(svn_patch_file_t **patch_file,
                   const char *diff, apr_pool_t *pool)
 {
+  apr_size_t bytes;
   apr_size_t len;
-  apr_status_t status;
+  const char *path;
   apr_file_t *apr_file;
 
   /* Create a patch file. */
-  status = apr_file_open(&apr_file, fname,
-                        (APR_READ | APR_WRITE | APR_CREATE | APR_TRUNCATE |
-                         APR_DELONCLOSE), APR_OS_DEFAULT, pool);
-  if (status != APR_SUCCESS)
-    return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "Cannot open '%s'",
-                             fname);
-  len = strlen(diff);
-  status = apr_file_write_full(apr_file, diff, len, &len);
-  if (status || len != strlen(diff))
+  SVN_ERR(svn_io_open_unique_file3(&apr_file, &path, NULL,
+                                   svn_io_file_del_on_pool_cleanup,
+                                   pool, pool));
+
+  bytes = strlen(diff);
+  SVN_ERR(svn_io_file_write_full(apr_file, diff, bytes, &len, pool));
+  if (len != bytes)
     return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
-                             "Cannot write to '%s'", fname);
-  SVN_ERR(svn_diff_open_patch_file(patch_file, fname, pool));
+                             "Cannot write to '%s'", path);
+  SVN_ERR(svn_io_file_close(apr_file, pool));
+  SVN_ERR(svn_diff_open_patch_file(patch_file, path, pool));
 
   return SVN_NO_ERROR;
 }
@@ -314,7 +314,6 @@ static svn_error_t *
 test_parse_unidiff(apr_pool_t *pool)
 {
   svn_patch_file_t *patch_file;
-  const char *fname = "test_parse_unidiff.patch";
   svn_boolean_t reverse;
   svn_boolean_t ignore_whitespace;
   int i;
@@ -330,7 +329,7 @@ test_parse_unidiff(apr_pool_t *pool)
 
       svn_pool_clear(iterpool);
 
-      SVN_ERR(create_patch_file(&patch_file, fname, unidiff, pool));
+      SVN_ERR(create_patch_file(&patch_file, unidiff, pool));
 
       /* We have two patches with one hunk each.
        * Parse the first patch. */
@@ -393,9 +392,8 @@ test_parse_git_diff(apr_pool_t *pool)
   svn_patch_file_t *patch_file;
   svn_patch_t *patch;
   svn_diff_hunk_t *hunk;
-  const char *fname = "test_parse_git_diff.patch";
 
-  SVN_ERR(create_patch_file(&patch_file, fname, git_unidiff, pool));
+  SVN_ERR(create_patch_file(&patch_file, git_unidiff, pool));
 
   /* Parse a deleted empty file */
   SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
@@ -467,10 +465,8 @@ test_parse_git_tree_and_text_diff(apr_po
   svn_patch_file_t *patch_file;
   svn_patch_t *patch;
   svn_diff_hunk_t *hunk;
-  const char *fname = "test_parse_git_tree_and_text_diff.patch";
 
-  SVN_ERR(create_patch_file(&patch_file, fname, git_tree_and_text_unidiff,
-                            pool));
+  SVN_ERR(create_patch_file(&patch_file, git_tree_and_text_unidiff, pool));
 
   /* Parse a copied file with text modifications. */
   SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
@@ -567,10 +563,8 @@ test_bad_git_diff_headers(apr_pool_t *po
   svn_patch_file_t *patch_file;
   svn_patch_t *patch;
   svn_diff_hunk_t *hunk;
-  const char *fname = "test_bad_git_diff_header.patch";
 
-  SVN_ERR(create_patch_file(&patch_file, fname, bad_git_diff_header,
-                            pool));
+  SVN_ERR(create_patch_file(&patch_file, bad_git_diff_header, pool));
 
   SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
                                     FALSE, /* reverse */
@@ -607,9 +601,8 @@ test_parse_property_diff(apr_pool_t *poo
   svn_prop_patch_t *prop_patch;
   svn_diff_hunk_t *hunk;
   apr_array_header_t *hunks;
-  const char *fname = "test_parse_property_diff.patch";
 
-  SVN_ERR(create_patch_file(&patch_file, fname, property_unidiff, pool));
+  SVN_ERR(create_patch_file(&patch_file, property_unidiff, pool));
 
   SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
                                     FALSE, /* reverse */
@@ -710,10 +703,8 @@ test_parse_property_and_text_diff(apr_po
   svn_prop_patch_t *prop_patch;
   svn_diff_hunk_t *hunk;
   apr_array_header_t *hunks;
-  const char *fname = "test_parse_property_and_text_diff.patch";
 
-  SVN_ERR(create_patch_file(&patch_file, fname, property_and_text_unidiff,
-                            pool));
+  SVN_ERR(create_patch_file(&patch_file, property_and_text_unidiff, pool));
 
   SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
                                     FALSE, /* reverse */
@@ -766,10 +757,8 @@ test_parse_diff_symbols_in_prop_unidiff(
   svn_prop_patch_t *prop_patch;
   svn_diff_hunk_t *hunk;
   apr_array_header_t *hunks;
-  const char *fname = "test_parse_diff_symbols_in_prop_unidiff.patch";
 
-  SVN_ERR(create_patch_file(&patch_file, fname, diff_symbols_in_prop_unidiff,
-                            pool));
+  SVN_ERR(create_patch_file(&patch_file, diff_symbols_in_prop_unidiff, pool));
 
   SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
                                     FALSE, /* reverse */
@@ -865,10 +854,8 @@ test_git_diffs_with_spaces_diff(apr_pool
 {
   svn_patch_file_t *patch_file;
   svn_patch_t *patch;
-  const char *fname = "test_git_diffs_with_spaces_diff.patch";
 
-  SVN_ERR(create_patch_file(&patch_file, fname, path_with_spaces_unidiff,
-                            pool));
+  SVN_ERR(create_patch_file(&patch_file, path_with_spaces_unidiff, pool));
 
   SVN_ERR(svn_diff_parse_next_patch(&patch, patch_file,
                                     FALSE, /* reverse */

Modified: subversion/branches/revprop-packing/subversion/tests/libsvn_subr/auth-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/libsvn_subr/auth-test.c?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/libsvn_subr/auth-test.c (original)
+++ subversion/branches/revprop-packing/subversion/tests/libsvn_subr/auth-test.c Thu Jul 28 21:58:49 2011
@@ -54,6 +54,9 @@ test_platform_specific_auth_providers(ap
 #ifdef SVN_HAVE_KWALLET
   number_of_providers += 2;
 #endif
+#ifdef SVN_HAVE_GPG_AGENT
+  number_of_providers += 1;
+#endif
 #ifdef SVN_HAVE_KEYCHAIN_SERVICES
   number_of_providers += 2;
 #endif

Modified: subversion/branches/revprop-packing/subversion/tests/libsvn_wc/db-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/libsvn_wc/db-test.c?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/libsvn_wc/db-test.c (original)
+++ subversion/branches/revprop-packing/subversion/tests/libsvn_wc/db-test.c Thu Jul 28 21:58:49 2011
@@ -228,6 +228,10 @@ static const char * const TESTING_DATA =
   "  1, null, 'file', '()', null, '$sha1$" SHA1_1 "', null, 2, " TIME_2s ", '" AUTHOR_2 "',"
   "  10, null, null, null);"
   "insert into nodes values ("
+  "  1, 'moved/file', 0, 'moved', 2, 'moved/file', 2, 'base-deleted',"
+  "  0, 'J/J-d', 'file', '()', null, '$sha1$" SHA1_1 "', null, 2, " TIME_2s ", '" AUTHOR_2 "',"
+  "  10, null, null, null);"
+  "insert into nodes values ("
   "  1, 'J/J-e', 1, 'J', null, null, null, 'normal',"
   "  0, 'other/place', 'dir', '()', null, null, null, null, null, null,"
   "  null, null, null, null);"
@@ -888,6 +892,8 @@ test_scan_addition(apr_pool_t *pool)
   const char *original_root_url;
   const char *original_uuid;
   svn_revnum_t original_revision;
+  const char *moved_from_abspath;
+  const char *delete_op_root_abspath;
 
   SVN_ERR(create_open(&db, &local_abspath, "test_scan_addition", pool));
 
@@ -896,7 +902,7 @@ test_scan_addition(apr_pool_t *pool)
             &status, &op_root_abspath,
             &repos_relpath, &repos_root_url, &repos_uuid,
             &original_repos_relpath, &original_root_url, &original_uuid,
-            &original_revision,
+            &original_revision, NULL, NULL,
             db, svn_dirent_join(local_abspath, "J", pool),
             pool, pool));
   SVN_TEST_ASSERT(status == svn_wc__db_status_added);
@@ -914,7 +920,7 @@ test_scan_addition(apr_pool_t *pool)
             &status, &op_root_abspath,
             &repos_relpath, &repos_root_url, &repos_uuid,
             &original_repos_relpath, &original_root_url, &original_uuid,
-            &original_revision,
+            &original_revision, NULL, NULL,
             db, svn_dirent_join(local_abspath, "J/J-a", pool),
             pool, pool));
   SVN_TEST_ASSERT(status == svn_wc__db_status_added);
@@ -932,12 +938,16 @@ test_scan_addition(apr_pool_t *pool)
             &status, &op_root_abspath,
             &repos_relpath, &repos_root_url, &repos_uuid,
             &original_repos_relpath, &original_root_url, &original_uuid,
-            &original_revision,
+            &original_revision, &moved_from_abspath, &delete_op_root_abspath,
             db, svn_dirent_join(local_abspath, "J/J-d", pool),
             pool, pool));
   SVN_TEST_ASSERT(status == svn_wc__db_status_moved_here);
   SVN_TEST_ASSERT(validate_abspath(local_abspath, "J/J-d",
                                    op_root_abspath, pool));
+  SVN_TEST_ASSERT(validate_abspath(local_abspath, "moved/file",
+                                   moved_from_abspath, pool));
+  SVN_TEST_ASSERT(validate_abspath(local_abspath, "moved/file",
+                                   delete_op_root_abspath, pool));
   SVN_TEST_STRING_ASSERT(repos_relpath, "J/J-d");
   SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE);
   SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE);
@@ -951,7 +961,7 @@ test_scan_addition(apr_pool_t *pool)
             &status, &op_root_abspath,
             &repos_relpath, &repos_root_url, &repos_uuid,
             &original_repos_relpath, &original_root_url, &original_uuid,
-            &original_revision,
+            &original_revision, NULL, NULL,
             db, svn_dirent_join(local_abspath, "J/J-b", pool),
             pool, pool));
   SVN_TEST_ASSERT(status == svn_wc__db_status_copied);
@@ -970,7 +980,7 @@ test_scan_addition(apr_pool_t *pool)
             &status, &op_root_abspath,
             &repos_relpath, &repos_root_url, &repos_uuid,
             &original_repos_relpath, &original_root_url, &original_uuid,
-            &original_revision,
+            &original_revision, NULL, NULL,
             db, svn_dirent_join(local_abspath, "J/J-b/J-b-a", pool),
             pool, pool));
   SVN_TEST_ASSERT(status == svn_wc__db_status_copied);
@@ -989,7 +999,7 @@ test_scan_addition(apr_pool_t *pool)
             &status, &op_root_abspath,
             &repos_relpath, &repos_root_url, &repos_uuid,
             &original_repos_relpath, &original_root_url, &original_uuid,
-            &original_revision,
+            &original_revision, NULL, NULL,
             db, svn_dirent_join(local_abspath, "J/J-b/J-b-b", pool),
             pool, pool));
   SVN_TEST_ASSERT(status == svn_wc__db_status_copied);

Modified: subversion/branches/revprop-packing/subversion/tests/libsvn_wc/op-depth-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/subversion/tests/libsvn_wc/op-depth-test.c?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/subversion/tests/libsvn_wc/op-depth-test.c (original)
+++ subversion/branches/revprop-packing/subversion/tests/libsvn_wc/op-depth-test.c Thu Jul 28 21:58:49 2011
@@ -40,6 +40,7 @@
 
 #include "private/svn_wc_private.h"
 #include "private/svn_sqlite.h"
+#include "private/svn_dep_compat.h"
 #include "../../libsvn_wc/wc.h"
 #include "../../libsvn_wc/wc_db.h"
 #define SVN_WC__I_AM_WC_DB
@@ -2784,7 +2785,7 @@ do_delete(svn_test__sandbox_t *b,
   SVN_ERR(insert_actual(b, actual_before));
   SVN_ERR(check_db_rows(b, "", before));
   SVN_ERR(check_db_actual(b, actual_before));
-  SVN_ERR(svn_wc__db_op_delete(b->wc_ctx->db, local_abspath,
+  SVN_ERR(svn_wc__db_op_delete(b->wc_ctx->db, local_abspath, NULL,
                                NULL, NULL /* notification */,
                                NULL, NULL /* cancellation */,
                                b->pool));

Modified: subversion/branches/revprop-packing/tools/dist/backport.pl
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/tools/dist/backport.pl?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/tools/dist/backport.pl (original)
+++ subversion/branches/revprop-packing/tools/dist/backport.pl Thu Jul 28 21:58:49 2011
@@ -28,6 +28,7 @@ my $SVN = $ENV{SVN} || 'svn'; # passed u
 my $VIM = 'vim';
 my $STATUS = './STATUS';
 my $BRANCHES = '^/subversion/branches';
+my $WET_RUN = qw[false true][1]; # don't commit
 
 sub usage {
   my $basename = $0;
@@ -61,27 +62,33 @@ sub merge {
   my %entry = @_;
 
   my ($logmsg_fh, $logmsg_filename) = tempfile();
-  my $mergeargs;
+  my ($mergeargs, $pattern);
 
   my $backupfile = "backport_pl.$$.tmp";
 
   if ($entry{branch}) {
+    # NOTE: This doesn't escape the branch into the pattern.
+    $pattern = printf '^ [*] %s branch\|Branch:\n *%s', $entry{branch}, $entry{branch};
     $mergeargs = "--reintegrate $BRANCHES/$entry{branch}";
-    print $logmsg_fh "Reintergrate the $BRANCHES/$entry{branch} branch:";
+    print $logmsg_fh "Reintergrate the $entry{header}:";
     print $logmsg_fh "";
-  } else {
+  } elsif (@{$entry{revisions}}) {
+    $pattern = 'r' . $entry{revisions}->[0];
     $mergeargs = join " ", (map { "-c$_" } @{$entry{revisions}}), '^/subversion/trunk';
     if (@{$entry{revisions}} > 1) {
-      print $logmsg_fh "Merge the r$entry{revisions}->[0] group from trunk:";
+      print $logmsg_fh "Merge the $entry{header} from trunk:";
       print $logmsg_fh "";
     } else {
       print $logmsg_fh "Merge r$entry{revisions}->[0] from trunk:";
       print $logmsg_fh "";
     }
+  } else {
+    die "Don't know how to call $entry{header}";
   }
   print $logmsg_fh $_ for @{$entry{entry}};
   close $logmsg_fh or die "Can't close $logmsg_filename: $!";
 
+  $pattern = '\V'.$pattern;
   my $script = <<"EOF";
 #!/bin/sh
 set -e
@@ -89,14 +96,24 @@ $SVN diff > $backupfile
 $SVN revert -R .
 $SVN up
 $SVN merge $mergeargs
-$VIM -e -s -n -N -i NONE -u NONE -c '/^ [*] r$entry{revisions}->[0]/normal! dap' -c wq $STATUS
-$SVN commit -F $logmsg_filename
+$VIM -e -s -n -N -i NONE -u NONE -c '/^ [*] $pattern/normal! dap' -c wq $STATUS
+if $WET_RUN; then
+  $SVN commit -F $logmsg_filename
+else
+  echo "Committing:"
+  $SVN status -q
+  cat $logmsg_filename
+fi
 EOF
 
   $script .= <<"EOF" if $entry{branch};
 reinteg_rev=\`$SVN info $STATUS | sed -ne 's/Last Changed Rev: //p'\`
-$SVN rm $BRANCHES/$entry{branch}\
-        -m "Remove the '$entry{branch}' branch, reintegrated in r\$reinteg_rev."
+if $WET_RUN; then
+  $SVN rm $BRANCHES/$entry{branch}\
+          -m "Remove the '$entry{branch}' branch, reintegrated in r\$reinteg_rev."
+else
+  echo "Removing reintegrated '$entry{branch}' branch"
+fi
 EOF
 
   open SHELL, '|-', qw#/bin/sh -x# or die $!;
@@ -107,6 +124,14 @@ EOF
   unlink $logmsg_filename unless $? or $!;
 }
 
+sub sanitize_branch {
+  local $_ = shift;
+  s#.*/##;
+  s/^\s*//;
+  s/\s*$//;
+  return $_;
+}
+
 # TODO: may need to parse other headers too?
 sub parse_entry {
   my @lines = @_;
@@ -118,6 +143,7 @@ sub parse_entry {
   s/^   // for @_;
 
   # revisions
+  $branch = sanitize_branch $1 if $_[0] =~ /^(\S*) branch$/;
   while ($_[0] =~ /^r/) {
     while ($_[0] =~ s/^r(\d+)(?:,\s*)?//) {
       push @revisions, $1;
@@ -135,16 +161,20 @@ sub parse_entry {
   # branch
   while (@_) {
     shift and next unless $_[0] =~ s/^Branch:\s*//;
-    $branch = (shift || shift || die "Branch header found without value");
-    $branch =~ s#.*/##;
-    $branch =~ s/^\s*//;
-    $branch =~ s/\s*$//;
+    $branch = sanitize_branch (shift || shift || die "Branch header found without value");
   }
 
+  # Compute a header.
+  my $header;
+  $header = "r$revisions[0] group" if @revisions;
+  $header = "$branch branch" if $branch;
+  warn "No header for [@lines]" unless $header;
+
   return (
     revisions => [@revisions],
     logsummary => [@logsummary],
     branch => $branch,
+    header => $header,
     votes => [@votes],
     entry => [@lines],
   );
@@ -154,7 +184,7 @@ sub handle_entry {
   my %entry = parse_entry @_;
 
   print "";
-  print "\n>>> The r$entry{revisions}->[0] group:";
+  print "\n>>> The $entry{header}:";
   print join ", ", map { "r$_" } @{$entry{revisions}};
   print "$BRANCHES/$entry{branch}" if $entry{branch};
   print "";

Modified: subversion/branches/revprop-packing/tools/dist/collect_sigs.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/tools/dist/collect_sigs.py?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/tools/dist/collect_sigs.py (original)
+++ subversion/branches/revprop-packing/tools/dist/collect_sigs.py Thu Jul 28 21:58:49 2011
@@ -199,24 +199,6 @@ def save_valid_sig(db, filename, keyid, 
 
   generate_asc_files(config.sigdir)
 
-  # Attempt to copy the results to a remote location
-  try:
-    import paramiko
-
-    client = paramiko.SSHClient()
-    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
-    client.connect(config.ssh['host'], username = config.ssh['user'],
-                   key_filename = config.ssh['key'])
-    sftp = client.open_sftp()
-
-    sftp.put(os.path.join(config.sigdir, filename + '.asc'),
-             os.path.join(config.ssh['dir'], config.version, 'deploy',
-                          filename + '.asc'))
-    client.close()
-  except:
-    # Ignore any errors
-    pass
-
 def verify_sig_for_file(signature, filename):
   args = ['gpg', '--logger-fd', '1', '--no-tty',
           '--status-fd', '2', '--verify', '-',

Modified: subversion/branches/revprop-packing/tools/dist/release.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/tools/dist/release.py?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/tools/dist/release.py (original)
+++ subversion/branches/revprop-packing/tools/dist/release.py Thu Jul 28 21:58:49 2011
@@ -45,6 +45,7 @@ import hashlib
 import tarfile
 import logging
 import datetime
+import tempfile
 import operator
 import itertools
 import subprocess
@@ -341,6 +342,38 @@ def build_env(args):
 #----------------------------------------------------------------------
 # Create release artifacts
 
+def fetch_changes(repos, branch, revision):
+    changes_peg_url = '%s/%s/CHANGES@%d' % (repos, branch, revision)
+    proc = subprocess.Popen(['svn', 'cat', changes_peg_url],
+                            stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    (stdout, stderr) = proc.communicate()
+    proc.wait()
+    return stdout.split('\n')
+
+
+def compare_changes(repos, branch, revision):
+    # Compare trunk's version of CHANGES with that of the branch,
+    # ignoring any lines in trunk's version precede what *should*
+    # match the contents of the branch's version.  (This allows us to
+    # continue adding new stuff at the top of trunk's CHANGES that
+    # might relate to the *next* major release line.)
+    branch_CHANGES = fetch_changes(repos, branch, revision)
+    trunk_CHANGES = fetch_changes(repos, 'trunk', revision)
+    try:
+        first_matching_line = trunk_CHANGES.index(branch_CHANGES[0])
+    except ValueError:
+        raise RuntimeError('CHANGES not synced between trunk and branch')
+
+    trunk_CHANGES = trunk_CHANGES[first_matching_line:]
+    saw_diff = False
+    import difflib
+    for diff_line in difflib.unified_diff(trunk_CHANGES, branch_CHANGES):
+        saw_diff = True
+        logging.debug('%s', diff_line)
+    if saw_diff:
+        raise RuntimeError('CHANGES not synced between trunk and branch')
+
+
 def roll_tarballs(args):
     'Create the release artifacts.'
     extns = ['zip', 'tar.gz', 'tar.bz2']
@@ -362,21 +395,10 @@ def roll_tarballs(args):
         if not dep.have_usable():
            raise RuntimeError('Cannot find usable %s' % dep.label)
 
-    # Make sure CHANGES is sync'd
     if branch != 'trunk':
-        trunk_CHANGES = '%s/trunk/CHANGES@%d' % (repos, args.revnum)
-        branch_CHANGES = '%s/%s/CHANGES@%d' % (repos, branch,
-                                                        args.revnum)
-        proc = subprocess.Popen(['svn', 'diff', '--summarize', branch_CHANGES,
-                                   trunk_CHANGES],
-                                  stdout=subprocess.PIPE,
-                                  stderr=subprocess.STDOUT)
-        (stdout, stderr) = proc.communicate()
-        proc.wait()
-
-        if stdout:
-            raise RuntimeError('CHANGES not synced between trunk and branch')
-
+        # Make sure CHANGES is sync'd.    
+        compare_changes(repos, branch, args.revnum)
+    
     # Create the output directory
     if not os.path.exists(get_deploydir(args.base_dir)):
         os.mkdir(get_deploydir(args.base_dir))
@@ -550,6 +572,61 @@ def write_announcement(args):
 
 
 #----------------------------------------------------------------------
+# Validate the signatures for a release
+
+key_start = '-----BEGIN PGP SIGNATURE-----\n'
+fp_pattern = re.compile(r'^pub\s+(\w+\/\w+)[^\n]*\n\s+Key\sfingerprint\s=((\s+[0-9A-F]{4}){10})\nuid\s+([^<\(]+)\s')
+
+def check_sigs(args):
+    'Check the signatures for the release.'
+
+    import gnupg
+    gpg = gnupg.GPG()
+
+    if args.target:
+        target = args.target
+    else:
+        target = os.path.join(os.getenv('HOME'), 'public_html', 'svn',
+                              str(args.version), 'deploy')
+
+    good_sigs = {}
+
+    for filename in glob.glob(os.path.join(target, 'subversion-*.asc')):
+        text = open(filename).read()
+        keys = text.split(key_start)
+
+        for key in keys[1:]:
+            fd, fn = tempfile.mkstemp()
+            os.write(fd, key_start + key)
+            os.close(fd)
+            verified = gpg.verify_file(open(fn, 'rb'), filename[:-4])
+            os.unlink(fn)
+
+            if verified.valid:
+                good_sigs[verified.key_id[-8:]] = True
+            else:
+                sys.stderr.write("BAD SIGNATURE for %s\n" % filename)
+                sys.exit(1)
+
+    for id in good_sigs.keys():
+        gpg = subprocess.Popen(['gpg', '--fingerprint', id],
+                               stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        rc = gpg.wait()
+        gpg_output = gpg.stdout.read()
+        if rc:
+            print(gpg_output)
+            sys.stderr.write("UNABLE TO GET FINGERPRINT FOR %s" % id)
+            sys.exit(1)
+
+        gpg_output = "\n".join([ l for l in gpg_output.splitlines()
+                                                     if l[0:7] != 'Warning' ])
+
+        fp = fp_pattern.match(gpg_output).groups()
+        print("   %s [%s] with fingerprint:" % (fp[3], fp[0]))
+        print("   %s" % fp[1])
+
+
+#----------------------------------------------------------------------
 # Main entry point for argument parsing and handling
 
 def main():
@@ -633,6 +710,17 @@ def main():
     subparser.add_argument('version', type=Version,
                     help='''The release label, such as '1.7.0-alpha1'.''')
 
+    # The check sigs subcommand
+    subparser = subparsers.add_parser('check-sigs',
+                    help='''Output to stdout the signatures collected for this
+                            release''')
+    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 destination used in
+                            'post-candiates'..''')
+
     # A meta-target
     subparser = subparsers.add_parser('clean',
                     help='''The same as the '--clean' switch, but as a

Modified: subversion/branches/revprop-packing/tools/dist/templates/rc-news.ezt
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/tools/dist/templates/rc-news.ezt?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/tools/dist/templates/rc-news.ezt (original)
+++ subversion/branches/revprop-packing/tools/dist/templates/rc-news.ezt Thu Jul 28 21:58:49 2011
@@ -4,7 +4,7 @@
     title="Link to this section">&para;</a> 
 </h3> 
  
-<p>We are please to announce to release of Apache Subversion [version].  This
+<p>We are pleased to announce to release of Apache Subversion [version].  This
    release is not intended for production use, but is provided as a milestone
    to encourage wider testing and feedback from intrepid users and maintainers.
    Please see the
@@ -13,7 +13,7 @@
    <a href="/docs/release-notes/[version_base].html">release notes</a> and 
    <a href="http://svn.apache.org/repos/asf/subversion/tags/[version]/CHANGES"> 
    change log</a> for information about what will eventually be
-   in the [version_base].0 release.</p> 
+   in the [version_base] release.</p> 
  
 <p>To get this release from the nearest mirror, please visit our
    <a href="/download/#pre-releases">download page</a>.</p> 

Modified: subversion/branches/revprop-packing/tools/server-side/svnpredumpfilter.py
URL: http://svn.apache.org/viewvc/subversion/branches/revprop-packing/tools/server-side/svnpredumpfilter.py?rev=1152016&r1=1152015&r2=1152016&view=diff
==============================================================================
--- subversion/branches/revprop-packing/tools/server-side/svnpredumpfilter.py (original)
+++ subversion/branches/revprop-packing/tools/server-side/svnpredumpfilter.py Thu Jul 28 21:58:49 2011
@@ -34,10 +34,16 @@ will be filtered by a user with universa
 repository's data.  Do not use the --use-merge-history (-g) or
 --stop-on-copy when generating this revision log stream.
 
+Return errorcode 0 if there are no additional dependencies found, 1 if
+there were; any other errorcode indicates a fatal error.
+
 Options:
 
    --help (-h)           Show this usage message and exit.
-   
+
+   --targets FILE        Read INCLUDE-PATHs and EXCLUDE-PATHs from FILE,
+                         one path per line.
+
    --verbose (-v)        Provide more information.  May be used multiple
                          times for additional levels of information (-vv).
 """
@@ -50,7 +56,11 @@ verbosity = 0
 
 class LogStreamError(Exception): pass
 class EOFError(Exception): pass
-  
+
+EXIT_SUCCESS = 0
+EXIT_MOREDEPS = 1
+EXIT_FAILURE = 2
+
 def sanitize_path(path):
   return '/'.join(filter(None, path.split('/')))
 
@@ -139,15 +149,21 @@ def svn_log_stream_get_dependencies(stre
   copy_action_re = re.compile(r'^   [AR] /(.*) \(from /(.*):[0-9]+\)$')
   line_buf = None
   last_revision = 0
+  eof = False
+  path_copies = {}
+  found_changed_path = False
   
-  while 1:
+  while not eof:
     try:
       line = line_buf is not None and line_buf or readline(stream)
     except EOFError:
       break
+
+    # We should be sitting at a log divider line.
     if line != '-' * 72:
       raise LogStreamError("Expected log divider line; not found.")
 
+    # Next up is a log header line.
     try:
       line = readline(stream)
     except EOFError:
@@ -155,7 +171,6 @@ def svn_log_stream_get_dependencies(stre
     match = header_re.search(line)
     if not match:
       raise LogStreamError("Expected log header line; not found.")
-
     pieces = map(string.strip, line.split('|'))
     revision = int(pieces[0][1:])
     if last_revision and revision >= last_revision:
@@ -170,51 +185,59 @@ def svn_log_stream_get_dependencies(stre
     else:
       log_lines = 0
 
+    # Now see if there are any changed paths.  If so, parse and process them.
     line = readline(stream)
-    if line != 'Changed paths:':
-      raise LogStreamError("Expected 'Changed paths:' line; not found.  Make "
-                           "sure log stream is from 'svn log' with the "
-                           "--verbose (-v) option.")
-
-    path_copies = {}
-    while 1:
-      try:
-        line = readline(stream)
-      except EOFError:
-        break
-      match = action_re.search(line)
-      if match:
-        match = copy_action_re.search(line)
+    if line == 'Changed paths:':
+      while 1:
+        try:
+          line = readline(stream)
+        except EOFError:
+          eof = True
+          break
+        match = action_re.search(line)
         if match:
-          path_copies[sanitize_path(match.group(1))] = sanitize_path(match.group(2))
-      else:
-        dt.handle_changes(path_copies)
-        if log_lines:
-          for i in range(log_lines):
-            readline(stream)
-          line_buf = None
+          found_changed_path = True
+          match = copy_action_re.search(line)
+          if match:
+            path_copies[sanitize_path(match.group(1))] = \
+              sanitize_path(match.group(2))
         else:
-          line_buf = line
-        break
+          break
+      dt.handle_changes(path_copies)
 
+    # Finally, skip any log message lines.  (If there are none,
+    # remember the last line we read, because it probably has
+    # something important in it.)
+    if log_lines:
+      for i in range(log_lines):
+        readline(stream)
+      line_buf = None
+    else:
+      line_buf = line
+
+  if not found_changed_path:
+    raise LogStreamError("No changed paths found; did you remember to run "
+                         "'svn log' with the --verbose (-v) option when "
+                         "generating the input to this script?")
+    
   return dt
 
 def analyze_logs(included_paths):
   print "Initial include paths:"
   for path in included_paths:
-    print "   /%s" % (path)
+    print " + /%s" % (path)
 
   dt = svn_log_stream_get_dependencies(sys.stdin, included_paths)
 
   if dt.dependent_paths:
+    found_new_deps = True
     print "Dependent include paths found:"
     for path in dt.dependent_paths:
-      print "   /%s" % (path)
+      print " + /%s" % (path)
     print "You need to also include them (or one of their parents)."
   else:
-    print "No new dependencies found!  You might still need " \
-          "to manually create parent directories for the " \
-          "included paths before loading a filtered dump:"
+    found_new_deps = False
+    print "No new dependencies found!"
     parents = {}
     for path in dt.include_paths:
       while 1:
@@ -224,9 +247,14 @@ def analyze_logs(included_paths):
         parents[parent] = 1
         path = parent
     parents = parents.keys()
-    parents.sort(compare_paths)
-    for parent in parents:
-      print "   /%s" % (parent)
+    if parents:
+      print "You might still need to manually create parent directories " \
+            "for the included paths before loading a filtered dump:"
+      parents.sort(compare_paths)
+      for parent in parents:
+        print "   /%s" % (parent)
+
+  return found_new_deps and EXIT_MOREDEPS or EXIT_SUCCESS
 
 def usage_and_exit(errmsg=None):
   program = os.path.basename(sys.argv[0])
@@ -234,14 +262,15 @@ def usage_and_exit(errmsg=None):
   stream.write(__doc__.replace("{PROGRAM}", program))
   if errmsg:
     stream.write("\nERROR: %s\n" % (errmsg))
-  sys.exit(errmsg and 1 or 0)
+  sys.exit(errmsg and EXIT_FAILURE or EXIT_SUCCESS)
 
 def main():
   config_dir = None
+  targets_file = None
   
   try:
     opts, args = getopt.getopt(sys.argv[1:], "hv",
-                               ["help", "verbose"])
+                               ["help", "verbose", "targets="])
   except getopt.GetoptError, e:
     usage_and_exit(str(e))
     
@@ -251,20 +280,39 @@ def main():
     elif option in ['-v', '--verbose']:
       global verbosity
       verbosity = verbosity + 1
+    elif option in ['--targets']:
+      targets_file = value
 
-  if len(args) < 2:
+  if len(args) == 0:
     usage_and_exit("Not enough arguments")
 
+  if targets_file is None:
+    targets = args[1:]
+  else:
+    targets = map(lambda x: x.rstrip('\n\r'),
+                  open(targets_file, 'r').readlines())
+  if not targets:
+    usage_and_exit("No target paths specified")
+
   try:
     if args[0] == 'include':
-      analyze_logs(map(sanitize_path, args[1:]))
+      sys.exit(analyze_logs(map(sanitize_path, targets)))
     elif args[0] == 'exclude':
       usage_and_exit("Feature not implemented")
     else:
       usage_and_exit("Valid subcommands are 'include' and 'exclude'")
+  except SystemExit:
+    raise
   except (LogStreamError, EOFError), e:
     log("ERROR: " + str(e), 0)
-    sys.exit(1)
+    sys.exit(EXIT_FAILURE)
+  except:
+    import traceback
+    exc_type, exc, exc_tb = sys.exc_info()
+    tb = traceback.format_exception(exc_type, exc, exc_tb)
+    sys.stderr.write(''.join(tb))
+    sys.exit(EXIT_FAILURE)
+
 
 if __name__ == "__main__":
     main()