You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2011/07/25 17:34:38 UTC

svn commit: r1150751 [5/5] - in /subversion/branches/gpg-agent-password-store: ./ build/ac-macros/ build/generator/swig/ notes/ subversion/bindings/swig/perl/native/t/ subversion/include/ subversion/include/private/ subversion/libsvn_client/ subversion...

Modified: subversion/branches/gpg-agent-password-store/subversion/tests/libsvn_client/client-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/subversion/tests/libsvn_client/client-test.c?rev=1150751&r1=1150750&r2=1150751&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/subversion/tests/libsvn_client/client-test.c (original)
+++ subversion/branches/gpg-agent-password-store/subversion/tests/libsvn_client/client-test.c Mon Jul 25 15:34:28 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/gpg-agent-password-store/subversion/tests/libsvn_diff/parse-diff-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/subversion/tests/libsvn_diff/parse-diff-test.c?rev=1150751&r1=1150750&r2=1150751&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/subversion/tests/libsvn_diff/parse-diff-test.c (original)
+++ subversion/branches/gpg-agent-password-store/subversion/tests/libsvn_diff/parse-diff-test.c Mon Jul 25 15:34:28 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/gpg-agent-password-store/subversion/tests/libsvn_wc/op-depth-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/subversion/tests/libsvn_wc/op-depth-test.c?rev=1150751&r1=1150750&r2=1150751&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/subversion/tests/libsvn_wc/op-depth-test.c (original)
+++ subversion/branches/gpg-agent-password-store/subversion/tests/libsvn_wc/op-depth-test.c Mon Jul 25 15:34:28 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

Modified: subversion/branches/gpg-agent-password-store/tools/dev/unix-build/Makefile.svn
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/tools/dev/unix-build/Makefile.svn?rev=1150751&r1=1150750&r2=1150751&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/tools/dev/unix-build/Makefile.svn (original)
+++ subversion/branches/gpg-agent-password-store/tools/dev/unix-build/Makefile.svn Mon Jul 25 15:34:28 2011
@@ -181,7 +181,7 @@ all: dirs-create bdb-install apr-install
 reset: dirs-reset bdb-reset apr-reset iconv-reset apr-util-reset \
 	httpd-reset neon-reset serf-reset serf-old-reset sqlite-reset \
 	cyrus-sasl-reset libmagic-reset ruby-reset python-reset \
-	bz2-install svn-reset
+	bz2-reset svn-reset
 
 # Use to save disk space.
 clean: bdb-clean apr-clean iconv-clean apr-util-clean httpd-clean \

Modified: subversion/branches/gpg-agent-password-store/tools/dist/backport.pl
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/tools/dist/backport.pl?rev=1150751&r1=1150750&r2=1150751&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/tools/dist/backport.pl (original)
+++ subversion/branches/gpg-agent-password-store/tools/dist/backport.pl Mon Jul 25 15:34:28 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/gpg-agent-password-store/tools/dist/release.py
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/tools/dist/release.py?rev=1150751&r1=1150750&r2=1150751&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/tools/dist/release.py (original)
+++ subversion/branches/gpg-agent-password-store/tools/dist/release.py Mon Jul 25 15:34:28 2011
@@ -341,6 +341,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 +394,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))

Modified: subversion/branches/gpg-agent-password-store/tools/dist/templates/rc-news.ezt
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/tools/dist/templates/rc-news.ezt?rev=1150751&r1=1150750&r2=1150751&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/tools/dist/templates/rc-news.ezt (original)
+++ subversion/branches/gpg-agent-password-store/tools/dist/templates/rc-news.ezt Mon Jul 25 15:34:28 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/gpg-agent-password-store/tools/server-side/svnpredumpfilter.py
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/tools/server-side/svnpredumpfilter.py?rev=1150751&r1=1150750&r2=1150751&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/tools/server-side/svnpredumpfilter.py (original)
+++ subversion/branches/gpg-agent-password-store/tools/server-side/svnpredumpfilter.py Mon Jul 25 15:34:28 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()