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 2012/02/02 11:03:56 UTC

svn commit: r1239513 [2/2] - in /subversion/branches/reintegrate-keep-alive: ./ subversion/bindings/swig/python/libsvn_swig_py/ subversion/bindings/swig/python/tests/ subversion/include/ subversion/include/private/ subversion/libsvn_client/ subversion/...

Modified: subversion/branches/reintegrate-keep-alive/subversion/mod_dav_svn/reports/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/mod_dav_svn/reports/update.c?rev=1239513&r1=1239512&r2=1239513&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/mod_dav_svn/reports/update.c (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/mod_dav_svn/reports/update.c Thu Feb  2 10:03:55 2012
@@ -955,8 +955,8 @@ dav_svn__update_report(const dav_resourc
           cdata = dav_xml_get_cdata(child, resource->pool, 0);
           if (! *cdata)
             return malformed_element_error(child->name, resource->pool);
-          if ((derr = dav_svn__test_canonical(cdata, resource->pool)))
-            return derr;
+          if (svn_path_is_url(cdata))
+            cdata = svn_uri_canonicalize(cdata, resource->pool);
           if ((serr = dav_svn__simple_parse_uri(&this_info, resource,
                                                 cdata, resource->pool)))
             return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
@@ -970,8 +970,8 @@ dav_svn__update_report(const dav_resourc
           cdata = dav_xml_get_cdata(child, resource->pool, 0);
           if (! *cdata)
             return malformed_element_error(child->name, resource->pool);
-          if ((derr = dav_svn__test_canonical(cdata, resource->pool)))
-            return derr;
+          if (svn_path_is_url(cdata))
+            cdata = svn_uri_canonicalize(cdata, resource->pool);
           if ((serr = dav_svn__simple_parse_uri(&this_info, resource,
                                                 cdata, resource->pool)))
             return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,

Modified: subversion/branches/reintegrate-keep-alive/subversion/svnrdump/dump_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/svnrdump/dump_editor.c?rev=1239513&r1=1239512&r2=1239513&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/svnrdump/dump_editor.c (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/svnrdump/dump_editor.c Thu Feb  2 10:03:55 2012
@@ -81,6 +81,9 @@ struct dump_edit_baton {
   /* The output stream we write the dumpfile to */
   svn_stream_t *stream;
 
+  /* A backdoor ra session to fetch additional information during the edit. */
+  svn_ra_session_t *ra_session;
+
   /* Pool for per-revision allocations */
   apr_pool_t *pool;
 
@@ -109,6 +112,9 @@ struct dump_edit_baton {
   svn_boolean_t dump_text;
   svn_boolean_t dump_props;
   svn_boolean_t dump_newlines;
+
+  /* The revision we're currently dumping. */
+  svn_revnum_t current_revision;
 };
 
 /* Make a directory baton to represent the directory at PATH (relative
@@ -854,14 +860,112 @@ fetch_base_func(const char **filename,
                 apr_pool_t *result_pool,
                 apr_pool_t *scratch_pool)
 {
-  *filename = NULL;
+  struct dump_edit_baton *eb = baton;
+  svn_stream_t *fstream;
+  svn_error_t *err;
+
+  if (path[0] == '/')
+    path += 1;
+
+  if (! SVN_IS_VALID_REVNUM(base_revision))
+    base_revision = eb->current_revision - 1;
+
+  SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL,
+                                 svn_io_file_del_on_pool_cleanup,
+                                 result_pool, scratch_pool));
+
+  err = svn_ra_get_file(eb->ra_session, path, base_revision,
+                        fstream, NULL, NULL, scratch_pool);
+  if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
+    {
+      svn_error_clear(err);
+      SVN_ERR(svn_stream_close(fstream));
+
+      *filename = NULL;
+      return SVN_NO_ERROR;
+    }
+  else if (err)
+    return svn_error_trace(err);
+  
+  SVN_ERR(svn_stream_close(fstream));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+fetch_props_func(apr_hash_t **props,
+                 void *baton,
+                 const char *path,
+                 svn_revnum_t base_revision,
+                 apr_pool_t *result_pool,
+                 apr_pool_t *scratch_pool)
+{
+  struct dump_edit_baton *eb = baton;
+  svn_node_kind_t node_kind;
+
+  if (path[0] == '/')
+    path += 1;
+
+  if (! SVN_IS_VALID_REVNUM(base_revision))
+    base_revision = eb->current_revision - 1;
+
+  SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, &node_kind,
+                            scratch_pool));
+
+  if (node_kind == svn_node_file)
+    {
+      SVN_ERR(svn_ra_get_file(eb->ra_session, path, base_revision,
+                              NULL, NULL, props, result_pool));
+    }
+  else if (node_kind == svn_node_dir)
+    {
+      apr_array_header_t *tmp_props;
+
+      SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, props, path,
+                              base_revision, 0 /* Dirent fields */,
+                              result_pool));
+      tmp_props = svn_prop_hash_to_array(*props, result_pool);
+      SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props,
+                                   result_pool));
+      *props = svn_prop_array_to_hash(tmp_props, result_pool);
+    }
+  else
+    {
+      *props = apr_hash_make(result_pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+fetch_kind_func(svn_kind_t *kind,
+                void *baton,
+                const char *path,
+                svn_revnum_t base_revision,
+                apr_pool_t *scratch_pool)
+{
+  struct dump_edit_baton *eb = baton;
+  svn_node_kind_t node_kind;
+
+  if (path[0] == '/')
+    path += 1;
+
+  if (! SVN_IS_VALID_REVNUM(base_revision))
+    base_revision = eb->current_revision - 1;
+
+  SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, &node_kind,
+                            scratch_pool));
+
+  *kind = svn__kind_from_node_kind(node_kind, FALSE);
   return SVN_NO_ERROR;
 }
 
 svn_error_t *
 svn_rdump__get_dump_editor(const svn_delta_editor_t **editor,
                            void **edit_baton,
+                           svn_revnum_t revision,
                            svn_stream_t *stream,
+                           svn_ra_session_t *ra_session,
                            svn_cancel_func_t cancel_func,
                            void *cancel_baton,
                            apr_pool_t *pool)
@@ -873,6 +977,8 @@ svn_rdump__get_dump_editor(const svn_del
 
   eb = apr_pcalloc(pool, sizeof(struct dump_edit_baton));
   eb->stream = stream;
+  eb->ra_session = ra_session;
+  eb->current_revision = revision;
 
   /* Create a special per-revision pool */
   eb->pool = svn_pool_create(pool);
@@ -906,6 +1012,8 @@ svn_rdump__get_dump_editor(const svn_del
                                             de, eb, editor, edit_baton, pool));
 
   shim_callbacks->fetch_base_func = fetch_base_func;
+  shim_callbacks->fetch_props_func = fetch_props_func;
+  shim_callbacks->fetch_kind_func = fetch_kind_func;
   shim_callbacks->fetch_baton = eb;
 
   SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,

Modified: subversion/branches/reintegrate-keep-alive/subversion/svnrdump/svnrdump.c
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/svnrdump/svnrdump.c?rev=1239513&r1=1239512&r2=1239513&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/svnrdump/svnrdump.c (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/svnrdump/svnrdump.c Thu Feb  2 10:03:55 2012
@@ -151,11 +151,11 @@ static const apr_getopt_option_t svnrdum
 
 /* Baton for the RA replay session. */
 struct replay_baton {
-  /* The editor producing diffs. */
-  const svn_delta_editor_t *editor;
+  /* A backdoor ra session for fetching information. */
+  svn_ra_session_t *extra_ra_session;
 
-  /* Baton for the editor. */
-  void *edit_baton;
+  /* The output stream */
+  svn_stream_t *stdout_stream;
 
   /* Whether to be quiet. */
   svn_boolean_t quiet;
@@ -220,10 +220,9 @@ replay_revstart(svn_revnum_t revision,
   SVN_ERR(svn_stream_printf(stdout_stream, pool, "\n"));
   SVN_ERR(svn_stream_close(stdout_stream));
 
-  /* Extract editor and editor_baton from the replay_baton and
-     set them so that the editor callbacks can use them. */
-  *editor = rb->editor;
-  *edit_baton = rb->edit_baton;
+  SVN_ERR(svn_rdump__get_dump_editor(editor, edit_baton, revision,
+                                     rb->stdout_stream, rb->extra_ra_session,
+                                     check_cancel, NULL, pool));
 
   return SVN_NO_ERROR;
 }
@@ -240,6 +239,9 @@ replay_revend(svn_revnum_t revision,
 {
   /* No resources left to free. */
   struct replay_baton *rb = replay_baton;
+
+  SVN_ERR(editor->close_edit(edit_baton, pool));
+
   if (! rb->quiet)
     SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n",
                                 revision));
@@ -343,6 +345,7 @@ dump_revision_header(svn_ra_session_t *s
  */
 static svn_error_t *
 replay_revisions(svn_ra_session_t *session,
+                 svn_ra_session_t *extra_ra_session,
                  const char *url,
                  svn_revnum_t start_revision,
                  svn_revnum_t end_revision,
@@ -350,20 +353,15 @@ replay_revisions(svn_ra_session_t *sessi
                  svn_boolean_t incremental,
                  apr_pool_t *pool)
 {
-  const svn_delta_editor_t *dump_editor;
   struct replay_baton *replay_baton;
-  void *dump_baton;
   const char *uuid;
   svn_stream_t *stdout_stream;
 
   SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
 
-  SVN_ERR(svn_rdump__get_dump_editor(&dump_editor, &dump_baton, stdout_stream,
-                                     check_cancel, NULL, pool));
-
   replay_baton = apr_pcalloc(pool, sizeof(*replay_baton));
-  replay_baton->editor = dump_editor;
-  replay_baton->edit_baton = dump_baton;
+  replay_baton->stdout_stream = stdout_stream;
+  replay_baton->extra_ra_session = extra_ra_session;
   replay_baton->quiet = quiet;
 
   /* Write the magic header and UUID */
@@ -401,6 +399,8 @@ replay_revisions(svn_ra_session_t *sessi
     {
       const svn_ra_reporter3_t *reporter;
       void *report_baton;
+      const svn_delta_editor_t *dump_editor;
+      void *dump_baton;
 
       /* First, we need to dump the start_revision in full.  We'll
          start with a revision record header. */
@@ -413,6 +413,10 @@ replay_revisions(svn_ra_session_t *sessi
          to the update reporter, telling it that we have nothing to
          start with.  The delta between nothing and everything-at-REV
          is, effectively, a full dump of REV. */
+      SVN_ERR(svn_rdump__get_dump_editor(&dump_editor, &dump_baton,
+                                         start_revision,
+                                         stdout_stream, extra_ra_session,
+                                         check_cancel, NULL, pool));
       SVN_ERR(svn_ra_do_update2(session, &reporter, &report_baton,
                                 start_revision, "", svn_depth_infinity,
                                 FALSE, dump_editor, dump_baton, pool));
@@ -531,7 +535,17 @@ dump_cmd(apr_getopt_t *os,
          apr_pool_t *pool)
 {
   opt_baton_t *opt_baton = baton;
-  return replay_revisions(opt_baton->session, opt_baton->url,
+  svn_ra_session_t *extra_ra_session;
+  const char *repos_root;
+
+  SVN_ERR(svn_client_open_ra_session(&extra_ra_session,
+                                     opt_baton->url,
+                                     opt_baton->ctx, pool));
+  SVN_ERR(svn_ra_get_repos_root2(extra_ra_session, &repos_root, pool));
+  SVN_ERR(svn_ra_reparent(extra_ra_session, repos_root, pool));
+
+  return replay_revisions(opt_baton->session, extra_ra_session,
+                          opt_baton->url,
                           opt_baton->start_revision.value.number,
                           opt_baton->end_revision.value.number,
                           opt_baton->quiet, opt_baton->incremental, pool);

Modified: subversion/branches/reintegrate-keep-alive/subversion/svnrdump/svnrdump.h
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/svnrdump/svnrdump.h?rev=1239513&r1=1239512&r2=1239513&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/svnrdump/svnrdump.h (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/svnrdump/svnrdump.h Thu Feb  2 10:03:55 2012
@@ -46,7 +46,9 @@ extern "C" {
 svn_error_t *
 svn_rdump__get_dump_editor(const svn_delta_editor_t **editor,
                            void **edit_baton,
+                           svn_revnum_t revision,
                            svn_stream_t *stream,
+                           svn_ra_session_t *ra_session,
                            svn_cancel_func_t cancel_func,
                            void *cancel_baton,
                            apr_pool_t *pool);

Modified: subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/commit_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/commit_tests.py?rev=1239513&r1=1239512&r2=1239513&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/commit_tests.py (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/commit_tests.py Thu Feb  2 10:03:55 2012
@@ -1136,19 +1136,26 @@ def commit_in_dir_scheduled_for_addition
 
   A_path = os.path.join(wc_dir, 'A')
   Z_path = os.path.join(wc_dir, 'Z')
+  Z_abspath = os.path.abspath(Z_path)
   mu_path = os.path.join(wc_dir, 'Z', 'mu')
 
   svntest.main.run_svn(None, 'move', A_path, Z_path)
+  
+  # Make sure mu is a committable
+  svntest.main.file_write(mu_path, "xxxx")
 
   # Commit a copied thing inside an added-with-history directory,
   # expecting a specific error to occur!
   svntest.actions.run_and_verify_commit(wc_dir,
                                         None,
                                         None,
-                                        "not known to exist in the repository",
+                                        "svn: E200009: '" +
+                                        re.escape(Z_abspath) +
+                                        "' is not known to exist in the",
                                         mu_path)
 
   Q_path = os.path.join(wc_dir, 'Q')
+  Q_abspath = os.path.abspath(Q_path)
   bloo_path = os.path.join(Q_path, 'bloo')
 
   os.mkdir(Q_path)
@@ -1160,9 +1167,24 @@ def commit_in_dir_scheduled_for_addition
   svntest.actions.run_and_verify_commit(wc_dir,
                                         None,
                                         None,
-                                        "not known to exist in the repository",
+                                        "svn: E200009: '" +
+                                        re.escape(Q_abspath) +
+                                        "' is not known to exist in the",
                                         bloo_path)
 
+  R_path = sbox.ospath('Z/B/R')
+  sbox.simple_mkdir('Z/B/R')
+
+  # Commit a d added thing inside an added directory,
+  # expecting a specific error to occur!
+  svntest.actions.run_and_verify_commit(wc_dir,
+                                        None,
+                                        None,
+                                        "svn: E200009: '" +
+                                        re.escape(Z_abspath) +
+                                        "' is not known to exist in the.*",
+                                        R_path)                                        
+
 #----------------------------------------------------------------------
 
 # Does this make sense now that deleted files are always removed from the wc?

Modified: subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/externals_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/externals_tests.py?rev=1239513&r1=1239512&r2=1239513&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/externals_tests.py (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/externals_tests.py Thu Feb  2 10:03:55 2012
@@ -2704,6 +2704,77 @@ def remap_file_external_with_prop_del(sb
   # http://subversion.tigris.org/issues/show_bug.cgi?id=4093#desc1
   svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)
 
+
+# Test for issue #4053 'svn:externals with explicit rev checks out HEAD'
+@Issue(4053)
+def dir_external_with_dash_r_only(sbox):
+  "whether '-r1 ^/A B' updates properly"
+  # svntest.factory.make(sbox,"""
+  #   echo 'newer alpha' > A/B/E/alpha
+  #   svn ci
+  #   svn ps svn:externals ' -r1 ^/A/B/E E_ext' .
+  #   svn up
+  #   # ^ move the 'status.tweak(wc_rev=2)' above the 'add()' call
+  #   svn info E_ext
+  #   # ^ change the 'svn info' call to
+  #   #  expected_info = { 'Revision': '1' }
+  #   #  actions.run_and_verify_info([expected_info], E_ext)
+  #   """)
+
+  sbox.build()
+  wc_dir = sbox.wc_dir
+  url = sbox.repo_url
+
+  A_B_E_alpha = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
+  E_ext = os.path.join(wc_dir, 'E_ext')
+
+  # echo 'newer alpha' > A/B/E/alpha
+  main.file_write(A_B_E_alpha, 'newer alpha\n')
+
+  # svn ci
+  expected_output = svntest.wc.State(wc_dir, {
+    'A/B/E/alpha'       : Item(verb='Sending'),
+  })
+
+  expected_status = actions.get_virginal_state(wc_dir, 1)
+  expected_status.tweak('A/B/E/alpha', wc_rev='2')
+
+  actions.run_and_verify_commit(wc_dir, expected_output, expected_status,
+    None, wc_dir)
+
+  # svn ps svn:externals ' -r1 ^/A/B/E E_ext' .
+  expected_stdout = ["property 'svn:externals' set on '" + wc_dir + "'\n"]
+
+  actions.run_and_verify_svn2('OUTPUT', expected_stdout, [], 0, 'ps',
+    'svn:externals', ' -r1 ^/A/B/E E_ext', wc_dir)
+
+  # svn up
+  expected_output = svntest.wc.State(wc_dir, {
+    'E_ext/beta'        : Item(status='A '),
+    'E_ext/alpha'       : Item(status='A '),
+  })
+
+  expected_disk = svntest.main.greek_state.copy()
+  expected_disk.add({
+    'E_ext'             : Item(),
+    'E_ext/alpha'       : Item(contents="This is the file 'alpha'.\n"),
+    'E_ext/beta'        : Item(contents="This is the file 'beta'.\n"),
+  })
+  expected_disk.tweak('A/B/E/alpha', contents='newer alpha\n')
+
+  expected_status.tweak(wc_rev='2')
+  expected_status.tweak('', status=' M')
+  expected_status.add({
+    'E_ext'             : Item(status='X '),
+  })
+
+  actions.run_and_verify_update(wc_dir, expected_output, expected_disk,
+    expected_status, None, None, None, None, None, False, wc_dir)
+
+  # svn info E_ext/alpha
+  expected_info = { 'Revision': '1' }
+  actions.run_and_verify_info([expected_info], E_ext)
+
 ########################################################################
 # Run the tests
 
@@ -2749,6 +2820,7 @@ test_list = [ None,
               include_immediate_dir_externals,
               shadowing,
               remap_file_external_with_prop_del,
+              dir_external_with_dash_r_only,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/merge_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/merge_tests.py?rev=1239513&r1=1239512&r2=1239513&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/merge_tests.py (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/merge_tests.py Thu Feb  2 10:03:55 2012
@@ -13449,6 +13449,12 @@ def natural_history_filtering(sbox):
   #
   # To set up a situation where this can occur we'll do the following:
   #
+  #     trunk   -1-----3-4-5-6-------8----------- A
+  #                \           \       \
+  #     branch1     2-----------\-------9-------- A_COPY
+  #                              \        \
+  #     branch2                   7--------10---- A_COPY_2
+  #
   #   1) Create a 'trunk'.
   #
   #   2) Copy 'trunk' to 'branch1'.

Modified: subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/svntest/factory.py
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/svntest/factory.py?rev=1239513&r1=1239512&r2=1239513&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/svntest/factory.py (original)
+++ subversion/branches/reintegrate-keep-alive/subversion/tests/cmdline/svntest/factory.py Thu Feb  2 10:03:55 2012
@@ -1612,6 +1612,11 @@ class TestFactory:
 
   def ensure_path_var(self, wc, pathelements):
     "Given a path in a working copy, make sure we have a variable for it."
+
+    # special case: if a path is '.', simply use wc_dir.
+    if pathelements == ['.']:
+      return wc.py, wc.realpath
+
     name = "_".join(pathelements)
 
     if wc.suffix is not None:

Modified: subversion/branches/reintegrate-keep-alive/tools/client-side/svnmucc/svnmucc.c
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/tools/client-side/svnmucc/svnmucc.c?rev=1239513&r1=1239512&r2=1239513&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/tools/client-side/svnmucc/svnmucc.c (original)
+++ subversion/branches/reintegrate-keep-alive/tools/client-side/svnmucc/svnmucc.c Thu Feb  2 10:03:55 2012
@@ -660,9 +660,9 @@ execute(const apr_array_header_t *action
   for (i = 0; i < actions->nelts; ++i)
     {
       struct action *action = APR_ARRAY_IDX(actions, i, struct action *);
+      const char *path1, *path2;
       switch (action->action)
         {
-          const char *path1, *path2;
         case ACTION_MV:
           path1 = subtract_anchor(anchor, action->path[0], pool);
           path2 = subtract_anchor(anchor, action->path[1], pool);

Modified: subversion/branches/reintegrate-keep-alive/tools/dist/backport.pl
URL: http://svn.apache.org/viewvc/subversion/branches/reintegrate-keep-alive/tools/dist/backport.pl?rev=1239513&r1=1239512&r2=1239513&view=diff
==============================================================================
--- subversion/branches/reintegrate-keep-alive/tools/dist/backport.pl (original)
+++ subversion/branches/reintegrate-keep-alive/tools/dist/backport.pl Thu Feb  2 10:03:55 2012
@@ -68,7 +68,7 @@ sub merge {
 
   if ($entry{branch}) {
     # NOTE: This doesn't escape the branch into the pattern.
-    $pattern = sprintf '\V\(%s branch\|branches\/%s\|Branch:\n *%s\)', $entry{branch}, $entry{branch}, $entry{branch};
+    $pattern = sprintf '\V\(%s branch(es)?\|branches\/%s\|Branch(es)?:\n *%s\)', $entry{branch}, $entry{branch}, $entry{branch};
     $mergeargs = "--reintegrate $BRANCHES/$entry{branch}";
     print $logmsg_fh "Reintergrate the $entry{header}:";
     print $logmsg_fh "";
@@ -158,7 +158,7 @@ sub parse_entry {
 
   # branch
   while (@_) {
-    shift and next unless $_[0] =~ s/^\s*Branch:\s*//;
+    shift and next unless $_[0] =~ s/^\s*Branch(es)?:\s*//;
     $branch = sanitize_branch (shift || shift || die "Branch header found without value");
   }
 
@@ -194,7 +194,7 @@ sub handle_entry {
 
   # TODO: this changes ./STATUS, which we're reading below, but
   #       on my system the loop in main() doesn't seem to care.
-  merge %entry if prompt;
+  merge %entry if $ENV{YES} or prompt;
 
   1;
 }