subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From julianf...@apache.org
Subject svn commit: r1205472 [1/2] - in /subversion/branches/showing-merge-info: ./ contrib/client-side/emacs/ notes/ subversion/libsvn_client/ subversion/libsvn_delta/ subversion/libsvn_fs_fs/ subversion/libsvn_ra_svn/ subversion/libsvn_subr/ subversion/libsv...
Date Wed, 23 Nov 2011 16:12:55 GMT
Author: julianfoad
Date: Wed Nov 23 16:12:52 2011
New Revision: 1205472

URL: http://svn.apache.org/viewvc?rev=1205472&view=rev
Log:
On the 'showing-merge-info' branch: Catch up to trunk@1205447.

Removed:
    subversion/branches/showing-merge-info/subversion/tests/libsvn_delta/editor-test.c
Modified:
    subversion/branches/showing-merge-info/   (props changed)
    subversion/branches/showing-merge-info/CHANGES
    subversion/branches/showing-merge-info/build.conf
    subversion/branches/showing-merge-info/contrib/client-side/emacs/dsvn.el
    subversion/branches/showing-merge-info/notes/repos-dictated-config
    subversion/branches/showing-merge-info/subversion/libsvn_client/commit.c
    subversion/branches/showing-merge-info/subversion/libsvn_client/merge.c
    subversion/branches/showing-merge-info/subversion/libsvn_client/mergeinfo.c
    subversion/branches/showing-merge-info/subversion/libsvn_client/mergeinfo.h
    subversion/branches/showing-merge-info/subversion/libsvn_delta/compat.c
    subversion/branches/showing-merge-info/subversion/libsvn_delta/editor.c
    subversion/branches/showing-merge-info/subversion/libsvn_fs_fs/fs_fs.c
    subversion/branches/showing-merge-info/subversion/libsvn_ra_svn/client.c
    subversion/branches/showing-merge-info/subversion/libsvn_subr/io.c
    subversion/branches/showing-merge-info/subversion/libsvn_wc/status.c
    subversion/branches/showing-merge-info/subversion/libsvn_wc/update_editor.c
    subversion/branches/showing-merge-info/subversion/libsvn_wc/util.c
    subversion/branches/showing-merge-info/subversion/libsvn_wc/wc.h
    subversion/branches/showing-merge-info/subversion/mod_dav_svn/mod_dav_svn.c
    subversion/branches/showing-merge-info/subversion/mod_dav_svn/repos.c
    subversion/branches/showing-merge-info/subversion/svn/status.c
    subversion/branches/showing-merge-info/subversion/tests/cmdline/mergeinfo_tests.py

Propchange: subversion/branches/showing-merge-info/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Wed Nov 23 16:12:52 2011
@@ -57,4 +57,4 @@
 /subversion/branches/tree-conflicts:868291-873154
 /subversion/branches/tree-conflicts-notify:873926-874008
 /subversion/branches/uris-as-urls:1060426-1064427
-/subversion/trunk:1177607-1203154
+/subversion/trunk:1177607-1205447

Modified: subversion/branches/showing-merge-info/CHANGES
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/CHANGES?rev=1205472&r1=1205471&r2=1205472&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/CHANGES (original)
+++ subversion/branches/showing-merge-info/CHANGES Wed Nov 23 16:12:52 2011
@@ -32,6 +32,37 @@ http://svn.apache.org/repos/asf/subversi
   - Bindings:
     *
 
+
+Version 1.7.2
+(?? ??? 2011, from /branches/1.7.x)
+http://svn.apache.org/repos/asf/subversion/tags/1.7.2
+
+  User-visible changes:
+   * fix working copy corruption after interrupted update/switch (issue #4040)
+   * avoid segfaults against pre-1.5 servers (r1186928)
+   * improve configure error message if apr-util uses old or no bdb (r1186784)
+   * make 'svn patch' ignore '/dev/null' targets for compat with git (r1197998)
+   * fix 'svn patch' segfault on patch that skips and deletes files (r1199950)
+   * omit "Committed revision N." output from 'svn commit --quiet' (r1200837)
+   * fix authz denial when svnserve root is a repository (issue #4060)
+   * fix uninitialized memory read in client diff code (r1201002)
+   * avoid potential segfault during merges (r1202807)
+   * fix an assertion failure when a symlink is updated (r1186944, -81, -83)
+   * make working copy operations fail if nodes have no base checksum (r1202630)
+   * fix nested <Location>s when using v2 protocol (r1203546, -651, -653)
+   * make mod_dav_svn ignore non-Subversion POST requests (r1187695)
+   * avoid reading freed memory (r1204478)
+   * recognize empty (only byte order mark) UTF-8 files as text (issue #4046)
+   * improve behavior when operating against a 1.0.x server (r1199876)
+
+  Developer-visible changes:
+   * JavaHL: allow 'status -u' to function properly (r1189190, -395)
+   * don't put '\r' characters in our generate sql headers (r1189580)
+   * properly define WIN64 on Windows x64 builds (r1188609)
+   * better adherence to C89 in enum definitions (r1189665)
+   * bump copyright year in Windows DLLs (r1189261)
+
+
 Version 1.7.1
 (24 Oct 2011, from /branches/1.7.x)
 http://svn.apache.org/repos/asf/subversion/tags/1.7.1

Modified: subversion/branches/showing-merge-info/build.conf
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/build.conf?rev=1205472&r1=1205471&r2=1205472&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/build.conf (original)
+++ subversion/branches/showing-merge-info/build.conf Wed Nov 23 16:12:52 2011
@@ -874,14 +874,6 @@ sources = random-test.c
 install = test
 libs = libsvn_test libsvn_delta libsvn_subr apriconv apr
 
-[editor-test]
-description = Test the editor v2 interface
-type = exe
-path = subversion/tests/libsvn_delta
-sources = editor-test.c
-install = test
-libs = libsvn_repos libsvn_fs libsvn_test libsvn_delta libsvn_subr apriconv apr
-
 [window-test]
 description = Test delta window generation
 type = exe
@@ -1132,7 +1124,7 @@ libs = __ALL__
        strings-reps-test changes-test locks-test repos-test
        checksum-test compat-test config-test hashdump-test mergeinfo-test
        opt-test path-test stream-test string-test time-test utf-test
-       target-test error-test cache-test editor-test spillbuf-test
+       target-test error-test cache-test spillbuf-test
        revision-test
        subst_translate-test
        translate-test

Modified: subversion/branches/showing-merge-info/contrib/client-side/emacs/dsvn.el
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/contrib/client-side/emacs/dsvn.el?rev=1205472&r1=1205471&r2=1205472&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/contrib/client-side/emacs/dsvn.el (original)
+++ subversion/branches/showing-merge-info/contrib/client-side/emacs/dsvn.el Wed Nov 23 16:12:52 2011
@@ -1161,7 +1161,8 @@ With prefix arg, prompt for REVISION."
     (?D . "Deleted")
     (?U . "Updated")
     (?G . "Merged")
-    (?C . "Conflict")))
+    (?C . "Conflict")
+    (?E . "Existed")))
 
 (defvar svn-merging nil)
 
@@ -1189,7 +1190,7 @@ With prefix arg, prompt for REVISION."
       (goto-char svn-output-marker)
       (while (not nomore)
         (cond ((looking-at
-                "\\([ ADUCG][ ADUCG][ B]\\)  \\(.*\\)\n")
+                "\\([ ADUCGE][ ADUCGE][ B]\\)  \\(.*\\)\n")
                (let* ((status (match-string 1))
                       (file-status (elt status 0))
                       (prop-status (elt status 1))

Modified: subversion/branches/showing-merge-info/notes/repos-dictated-config
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/notes/repos-dictated-config?rev=1205472&r1=1205471&r2=1205472&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/notes/repos-dictated-config (original)
+++ subversion/branches/showing-merge-info/notes/repos-dictated-config Wed Nov 23 16:12:52 2011
@@ -4,9 +4,9 @@ Introduction
 ============
 Many software development shops of non-trivial size desire to have an enforce
 a uniform configuration environment among the various clients which commit
-to their repositories.  Although these shops my have the ability to control
+to their repositories.  Although these shops may have the ability to control
 the environment on the client machines (dictating software versions, etc),
-relying up on the client for setting various configuration parameters can
+relying upon the client for setting various configuration parameters can
 be time-consuming and problematic.
 
 Subversion already provides the means of enforcing much (but not all) of this

Modified: subversion/branches/showing-merge-info/subversion/libsvn_client/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_client/commit.c?rev=1205472&r1=1205471&r2=1205472&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_client/commit.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_client/commit.c Wed Nov 23 16:12:52 2011
@@ -145,19 +145,22 @@ send_file_contents(const char *path,
       if (svn_subst_translation_required(eol_style, eol, keywords,
                                          FALSE, TRUE))
         {
-          svn_boolean_t repair = FALSE;
+          if (eol_style == svn_subst_eol_style_unknown)
+            return svn_error_createf(SVN_ERR_IO_UNKNOWN_EOL, NULL,
+                                    _("%s property on '%s' contains "
+                                      "unrecognized EOL-style '%s'"),
+                                    SVN_PROP_EOL_STYLE, path,
+                                    eol_style_val->data);
 
+          /* We're importing, so translate files with 'native' eol-style to
+           * repository-normal form, not to this platform's native EOL. */
           if (eol_style == svn_subst_eol_style_native)
             eol = SVN_SUBST_NATIVE_EOL_STR;
-          else if (eol_style == svn_subst_eol_style_fixed)
-            repair = TRUE;
-          else if (eol_style != svn_subst_eol_style_none)
-            return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL);
 
           /* Wrap the working copy stream with a filter to detranslate it. */
           contents = svn_subst_stream_translated(contents,
                                                  eol,
-                                                 repair,
+                                                 TRUE /* repair */,
                                                  keywords,
                                                  FALSE /* expand */,
                                                  pool);
@@ -822,7 +825,7 @@ svn_client_import4(const char *path,
                     ignore_unknown_node_types, ctx, subpool)))
     {
       svn_error_clear(editor->abort_edit(edit_baton, subpool));
-      return err;
+      return svn_error_trace(err);
     }
 
   svn_pool_destroy(subpool);

Modified: subversion/branches/showing-merge-info/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/showing-merge-info/subversion/libsvn_client/merge.c?rev=1205472&r1=1205471&r2=1205472&view=diff
==============================================================================
--- subversion/branches/showing-merge-info/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/showing-merge-info/subversion/libsvn_client/merge.c Wed Nov 23 16:12:52 2011
@@ -593,6 +593,7 @@ tree_conflict(merge_cmd_baton_t *merge_b
 
       if (merge_b->conflicted_paths == NULL)
         merge_b->conflicted_paths = apr_hash_make(merge_b->pool);
+      victim_abspath = apr_pstrdup(merge_b->pool, victim_abspath);
 
       apr_hash_set(merge_b->conflicted_paths, victim_abspath,
                    APR_HASH_KEY_STRING, victim_abspath);
@@ -634,6 +635,7 @@ tree_conflict_on_add(merge_cmd_baton_t *
 
       if (merge_b->conflicted_paths == NULL)
         merge_b->conflicted_paths = apr_hash_make(merge_b->pool);
+      victim_abspath = apr_pstrdup(merge_b->pool, victim_abspath);
 
       apr_hash_set(merge_b->conflicted_paths, victim_abspath,
                    APR_HASH_KEY_STRING, victim_abspath);
@@ -664,6 +666,7 @@ tree_conflict_on_add(merge_cmd_baton_t *
 
       if (merge_b->conflicted_paths == NULL)
         merge_b->conflicted_paths = apr_hash_make(merge_b->pool);
+      victim_abspath = apr_pstrdup(merge_b->pool, victim_abspath);
 
       apr_hash_set(merge_b->conflicted_paths, victim_abspath,
                    APR_HASH_KEY_STRING, victim_abspath);
@@ -1252,6 +1255,8 @@ merge_props_changed(svn_wc_notify_state_
       else if (err)
         return svn_error_trace(err);
     }
+  else if (state)
+    *state = svn_wc_notify_state_unchanged;
 
   return SVN_NO_ERROR;
 }
@@ -1460,6 +1465,9 @@ merge_file_changed(svn_wc_notify_state_t
   SVN_ERR_ASSERT(!older_abspath || svn_dirent_is_absolute(older_abspath));
   SVN_ERR_ASSERT(!yours_abspath || svn_dirent_is_absolute(yours_abspath));
 
+  if (tree_conflicted)
+    *tree_conflicted = FALSE;
+
   /* Check for an obstructed or missing node on disk. */
   {
     svn_wc_notify_state_t obstr_state;
@@ -1690,6 +1698,9 @@ merge_file_added(svn_wc_notify_state_t *
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(mine_abspath));
 
+  if (tree_conflicted)
+    *tree_conflicted = FALSE;
+
   /* Easy out: We are only applying mergeinfo differences. */
   if (merge_b->record_only)
     {
@@ -2014,6 +2025,9 @@ merge_file_deleted(svn_wc_notify_state_t
                                              mine_relpath, scratch_pool);
   svn_node_kind_t kind;
 
+  if (tree_conflicted)
+    *tree_conflicted = FALSE;
+
   if (merge_b->dry_run)
     {
       const char *wcpath = apr_pstrdup(merge_b->pool, mine_abspath);
@@ -3216,18 +3230,18 @@ adjust_deleted_subtree_ranges(svn_client
 
 /* Helper for do_directory_merge().
 
-   URL1, REVISION1, URL2, REVISION2, NOTIFY_B, and MERGE_B are
+   URL1, REVISION1, URL2, REVISION2, and MERGE_B are
    cascaded from the arguments of the same name in do_directory_merge().
    RA_SESSION is the session for the younger of URL1@REVISION1 and
    URL2@REVISION2.
 
-   Adjust the subtrees in NOTIFY_B->CHILDREN_WITH_MERGEINFO so that we don't
+   Adjust the subtrees in CHILDREN_WITH_MERGEINFO so that we don't
    later try to describe invalid paths in drive_merge_report_editor().
    This function is just a thin wrapper around
    adjust_deleted_subtree_ranges(), which see for further details.
 
    SCRATCH_POOL is used for all temporary allocations.  Changes to
-   NOTIFY_B->CHILDREN_WITH_MERGEINFO are allocated in RESULT_POOL.
+   CHILDREN_WITH_MERGEINFO are allocated in RESULT_POOL.
 */
 static svn_error_t *
 fix_deleted_subtree_ranges(const char *url1,
@@ -3235,7 +3249,7 @@ fix_deleted_subtree_ranges(const char *u
                            const char *url2,
                            svn_revnum_t revision2,
                            svn_ra_session_t *ra_session,
-                           notification_receiver_baton_t *notify_b,
+                           apr_array_header_t *children_with_mergeinfo,
                            merge_cmd_baton_t *merge_b,
                            apr_pool_t *result_pool,
                            apr_pool_t *scratch_pool)
@@ -3244,13 +3258,12 @@ fix_deleted_subtree_ranges(const char *u
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
   svn_boolean_t is_rollback = revision2 < revision1;
 
-  /* NOTIFY_B->CHILDREN_WITH_MERGEINFO is sorted in depth-first order, so
+  /* CHILDREN_WITH_MERGEINFO is sorted in depth-first order, so
      start at index 1 to examine only subtrees. */
-  for (i = 1; i < notify_b->children_with_mergeinfo->nelts; i++)
+  for (i = 1; i < children_with_mergeinfo->nelts; i++)
     {
       svn_client__merge_path_t *child =
-        APR_ARRAY_IDX(notify_b->children_with_mergeinfo, i,
-                      svn_client__merge_path_t *);
+        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
       svn_client__merge_path_t *parent;
       apr_array_header_t *deleted_rangelist, *added_rangelist;
       int parent_index;
@@ -3262,9 +3275,9 @@ fix_deleted_subtree_ranges(const char *u
       svn_pool_clear(iterpool);
 
       /* Find CHILD's parent. */
-      parent_index = find_nearest_ancestor(notify_b->children_with_mergeinfo,
+      parent_index = find_nearest_ancestor(children_with_mergeinfo,
                                            FALSE, child->abspath);
-      parent = APR_ARRAY_IDX(notify_b->children_with_mergeinfo, parent_index,
+      parent = APR_ARRAY_IDX(children_with_mergeinfo, parent_index,
                              svn_client__merge_path_t *);
 
       /* Since CHILD is a subtree then its parent must be in
@@ -3393,18 +3406,15 @@ get_full_mergeinfo(svn_mergeinfo_t *reco
   /* First, we get the real mergeinfo. */
   if (recorded_mergeinfo)
     {
-      svn_boolean_t inherited_mergeinfo;
       svn_boolean_t inherited_from_repos;
 
       SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(recorded_mergeinfo,
-                                                    &inherited_mergeinfo,
+                                                    inherited,
                                                     &inherited_from_repos,
                                                     FALSE,
                                                     inherit, ra_session,
                                                     target_abspath,
                                                     ctx, result_pool));
-      if (inherited)
-        *inherited = inherited_mergeinfo;
     }
 
   if (implicit_mergeinfo)
@@ -3541,7 +3551,7 @@ inherit_implicit_mergeinfo_from_parent(s
    SCRATCH_POOL for all temporary allocations.
 
    PARENT, CHILD, REVISION1, REVISION2, RA_SESSION, and
-   CTX are all cascased from the arguments of the same name in
+   CTX are all cascaded from the arguments of the same name in
    filter_merged_revisions() and the same conditions for that function
    hold here. */
 static svn_error_t *
@@ -4545,8 +4555,7 @@ update_wc_mergeinfo(svn_mergeinfo_catalo
          mergeinfo that path inherits. */
       if (mergeinfo == NULL && ranges->nelts == 0)
         {
-          svn_boolean_t inherited;
-          SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, &inherited,
+          SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, NULL,
                                                svn_mergeinfo_nearest_ancestor,
                                                local_abspath, NULL, NULL,
                                                FALSE, ctx, iterpool, iterpool));
@@ -4631,11 +4640,11 @@ update_wc_mergeinfo(svn_mergeinfo_catalo
 
    Record override mergeinfo on any paths skipped during a merge.
 
-   Set empty mergeinfo on each path in NOTIFY_B->SKIPPED_PATHS so the path
+   Set empty mergeinfo on each path in NOTIFY_B->SKIPPED_ABSPATHS so the path
    does not incorrectly inherit mergeinfo that will later be describing
    the merge.
 
-   MERGEINFO_PATH, NOTIFY_B, and MERGE_B are all cascased from
+   MERGEINFO_PATH, NOTIFY_B, and MERGE_B are all cascaded from
    arguments of the same name in the caller.
 
    IS_ROLLBACK is true if the caller is recording a reverse merge and false
@@ -4734,8 +4743,7 @@ make_merge_conflict_error(const char *ta
    or are descendants of TARGET_WCPATH by setting those children to NULL. */
 static void
 remove_absent_children(const char *target_wcpath,
-                       apr_array_header_t *children_with_mergeinfo,
-                       notification_receiver_baton_t *notify_b)
+                       apr_array_header_t *children_with_mergeinfo)
 {
   /* Before we try to override mergeinfo for skipped paths, make sure
      the path isn't absent due to authz restrictions, because there's
@@ -4744,8 +4752,7 @@ remove_absent_children(const char *targe
   for (i = 0; i < children_with_mergeinfo->nelts; i++)
     {
       svn_client__merge_path_t *child =
-        APR_ARRAY_IDX(children_with_mergeinfo,
-                      i, svn_client__merge_path_t *);
+        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
       if ((child->absent || child->scheduled_for_deletion)
           && svn_dirent_is_ancestor(target_wcpath, child->abspath))
         {
@@ -4754,38 +4761,36 @@ remove_absent_children(const char *targe
     }
 }
 
-/* Helper for do_directory_merge() to handle the case were a merge editor
+/* Helper for do_directory_merge() to handle the case where a merge editor
    drive removes explicit mergeinfo from a subtree of the merge target.
 
-   MERGE_B, NOTIFY_B are cascaded from the arguments of the same name in
+   MERGE_B is cascaded from the argument of the same name in
    do_directory_merge().  If MERGE_B->DRY_RUN is true do nothing, if it is
    false then for each path (if any) in MERGE_B->PATHS_WITH_DELETED_MERGEINFO
-   remove that path from NOTIFY_B->CHILDREN_WITH_MERGEINFO by setting that
+   remove that path from CHILDREN_WITH_MERGEINFO by setting that
    child to NULL.  The one exception is for the merge target itself,
    MERGE_B->TARGET_ABSPATH, this must always be present in
-   NOTIFY_B->CHILDREN_WITH_MERGEINFO so this is never removed by this
+   CHILDREN_WITH_MERGEINFO so this is never removed by this
    function. */
 static void
 remove_children_with_deleted_mergeinfo(merge_cmd_baton_t *merge_b,
-                                       notification_receiver_baton_t *notify_b)
+                                       apr_array_header_t *children_with_mergeinfo)
 {
   int i;
 
   if (merge_b->dry_run || !merge_b->paths_with_deleted_mergeinfo)
     return;
 
-  /* NOTIFY_B->CHILDREN_WITH_MERGEINFO[0] is the always the merge target
+  /* CHILDREN_WITH_MERGEINFO[0] is the always the merge target
      so start at the first child. */
-  for (i = 1; i < notify_b->children_with_mergeinfo->nelts; i++)
+  for (i = 1; i < children_with_mergeinfo->nelts; i++)
     {
       svn_client__merge_path_t *child =
-        APR_ARRAY_IDX(notify_b->children_with_mergeinfo,
-                      i, svn_client__merge_path_t *);
+        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
       if (apr_hash_get(merge_b->paths_with_deleted_mergeinfo,
-                       child->abspath,
-                       APR_HASH_KEY_STRING))
+                       child->abspath, APR_HASH_KEY_STRING))
         {
-          svn_sort__array_delete(notify_b->children_with_mergeinfo, i--, 1);
+          svn_sort__array_delete(children_with_mergeinfo, i--, 1);
         }
     }
 }
@@ -5071,77 +5076,49 @@ drive_merge_report_editor(const char *ta
 }
 
 /* Iterate over each svn_client__merge_path_t * element in
-   CHILDREN_WITH_MERGEINFO and find the most inclusive start revision
-   among those element's first remaining_ranges element.
-
-   If IS_ROLLBACK is true the youngest revision is considered the "most
-   inclusive" otherwise the oldest revision is.
+   CHILDREN_WITH_MERGEINFO and, if START_REV is true, find the most inclusive
+   start revision among those element's first remaining_ranges element.  If
+   START_REV is false, then look for the most inclusive end revision.
+
+   If IS_ROLLBACK is true the youngest start or end (as per START_REV)
+   revision is considered the "most inclusive" otherwise the oldest revision
+   is.
 
    If none of CHILDREN_WITH_MERGEINFO's elements have any remaining ranges
    return SVN_INVALID_REVNUM. */
 static svn_revnum_t
-get_most_inclusive_start_rev(const apr_array_header_t *children_with_mergeinfo,
-                             svn_boolean_t is_rollback)
+get_most_inclusive_rev(const apr_array_header_t *children_with_mergeinfo,
+                       svn_boolean_t is_rollback,
+                       svn_boolean_t start_rev)
 {
   int i;
-  svn_revnum_t start_rev = SVN_INVALID_REVNUM;
+  svn_revnum_t most_inclusive_rev = SVN_INVALID_REVNUM;
 
   for (i = 0; i < children_with_mergeinfo->nelts; i++)
     {
       svn_client__merge_path_t *child =
         APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
-      svn_merge_range_t *range;
 
       if ((! child) || child->absent)
         continue;
-      if (! child->remaining_ranges->nelts)
-        continue;
-      range = APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
-      if ((i == 0) && (range->start == range->end))
-        continue;
-      if ((start_rev == SVN_INVALID_REVNUM)
-          || (is_rollback && (range->start > start_rev))
-          || ((! is_rollback) && (range->start < start_rev)))
-        start_rev = range->start;
-    }
-  return start_rev;
-}
-
-/* Iterate over each svn_client__merge_path_t * element in
-   CHILDREN_WITH_MERGEINFO and find the most inclusive end revision
-   among those element's first remaining_ranges element.
-
-   If IS_ROLLBACK is true the oldest revision is considered the "most
-   inclusive" otherwise the youngest revision is.
-
-   If none of CHILDREN_WITH_MERGEINFO's elements have any remaining ranges
-   return SVN_INVALID_REVNUM. */
-static svn_revnum_t
-get_most_inclusive_end_rev(const apr_array_header_t *children_with_mergeinfo,
-                           svn_boolean_t is_rollback)
-{
-  int i;
-  svn_revnum_t end_rev = SVN_INVALID_REVNUM;
-
-  for (i = 0; i < children_with_mergeinfo->nelts; i++)
-    {
-      svn_client__merge_path_t *child =
-        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
-      if (!child || child->absent)
-        continue;
       if (child->remaining_ranges->nelts > 0)
         {
-          svn_merge_range_t *range = APR_ARRAY_IDX(child->remaining_ranges, 0,
-                                                   svn_merge_range_t *);
-          if ((end_rev == SVN_INVALID_REVNUM)
-              || (is_rollback && (range->end > end_rev))
-              || ((! is_rollback) && (range->end < end_rev)))
-            end_rev = range->end;
+          svn_merge_range_t *range =
+            APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
+
+          /* Are we looking for the most inclusive start or end rev? */
+          svn_revnum_t rev = start_rev ? range->start : range->end;
+
+          if ((most_inclusive_rev == SVN_INVALID_REVNUM)
+              || (is_rollback && (rev > most_inclusive_rev))
+              || ((! is_rollback) && (rev < most_inclusive_rev)))
+            most_inclusive_rev = rev;
         }
     }
-  return end_rev;
+  return most_inclusive_rev;
 }
 
+
 /* If first item in each child of CHILDREN_WITH_MERGEINFO's
    remaining_ranges is inclusive of END_REV, Slice the first range in
    to two at END_REV. All the allocations are persistent and allocated
@@ -5168,29 +5145,15 @@ slice_remaining_ranges(apr_array_header_
               || (!is_rollback && (range->start < end_rev)
                   && (range->end > end_rev)))
             {
-              int j;
               svn_merge_range_t *split_range1, *split_range2;
-              apr_array_header_t *orig_remaining_ranges =
-                                                     child->remaining_ranges;
+
               split_range1 = svn_merge_range_dup(range, pool);
               split_range2 = svn_merge_range_dup(range, pool);
               split_range1->end = end_rev;
               split_range2->start = end_rev;
-              child->remaining_ranges =
-                     apr_array_make(pool, (child->remaining_ranges->nelts + 1),
-                                    sizeof(svn_merge_range_t *));
-              APR_ARRAY_PUSH(child->remaining_ranges,
-                             svn_merge_range_t *) = split_range1;
-              APR_ARRAY_PUSH(child->remaining_ranges,
-                             svn_merge_range_t *) = split_range2;
-              for (j = 1; j < orig_remaining_ranges->nelts; j++)
-                {
-                  svn_merge_range_t *orig_range =
-                                     APR_ARRAY_IDX(orig_remaining_ranges, j,
-                                                   svn_merge_range_t *);
-                  APR_ARRAY_PUSH(child->remaining_ranges,
-                                 svn_merge_range_t *) = orig_range;
-                }
+              APR_ARRAY_IDX(child->remaining_ranges, 0,
+                            svn_merge_range_t *) = split_range1;
+              svn_sort__array_insert(&split_range2, child->remaining_ranges, 1);
             }
         }
     }
@@ -5206,9 +5169,11 @@ slice_remaining_ranges(apr_array_header_
    new remaining_ranges array in POOL.
 
    ### TODO: We should have remaining_ranges in reverse order to avoid
-   ### recreating and reallocationg the remaining_ranges every time we want
+   ### recreating and reallocating the remaining_ranges every time we want
    ### to remove the first range.  If the ranges were reversed we could simply
-   ### pop the last element in the array. */
+   ### pop the last element in the array.  Alternatively we might be able to
+   ### make svn_sort__array_delete() efficient: it could increment 'elts'
+   ### (and decrement 'nelts' and 'nalloc') instead of moving elements. */
 static void
 remove_first_range_from_remaining_ranges(svn_revnum_t revision,
                                          apr_array_header_t
@@ -5230,22 +5195,7 @@ remove_first_range_from_remaining_ranges
             APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
           if (first_range->end == revision)
             {
-              apr_array_header_t *orig_remaining_ranges =
-                child->remaining_ranges;
-              int j;
-
-              child->remaining_ranges =
-                apr_array_make(pool, (child->remaining_ranges->nelts - 1),
-                               sizeof(svn_merge_range_t *));
-              for (j = 1; j < orig_remaining_ranges->nelts; j++)
-                {
-                  svn_merge_range_t *range =
-                    APR_ARRAY_IDX(orig_remaining_ranges,
-                                  j,
-                                  svn_merge_range_t *);
-                  APR_ARRAY_PUSH(child->remaining_ranges,
-                                 svn_merge_range_t *) = range;
-                }
+              svn_sort__array_delete(child->remaining_ranges, 0, 1);
             }
         }
     }
@@ -5358,7 +5308,13 @@ get_child_with_mergeinfo(const apr_array
 /* Insert a deep copy of INSERT_ELEMENT into the CHILDREN_WITH_MERGEINFO
    array at its correct position.  Allocate the new storage in POOL.
    CHILDREN_WITH_MERGEINFO is a depth first sorted array of
-   (svn_client__merge_path_t *). */
+   (svn_client__merge_path_t *).
+
+   ### Most callers don't need this to deep-copy the new element.
+   ### It may be more efficient for some callers to insert a bunch of items
+       out of order and then sort afterwards. (One caller is doing a qsort
+       after calling this anyway.)
+ */
 static void
 insert_child_to_merge(apr_array_header_t *children_with_mergeinfo,
                       const svn_client__merge_path_t *insert_element,
@@ -5582,6 +5538,71 @@ pre_merge_status_cb(void *baton,
   return SVN_NO_ERROR;
 }
 
+/* Find all the subtrees in the working copy tree rooted at TARGET_ABSPATH
+ * that have explicit mergeinfo.
+ * Set *SUBTREES_WITH_MERGEINFO to a hash mapping (const char *) absolute
+ * WC path to (svn_mergeinfo_t *) mergeinfo.
+ *
+ * ### Is this function equivalent to:
+ *
+ *   svn_client__get_wc_mergeinfo_catalog(
+ *     subtrees_with_mergeinfo, inherited=NULL, include_descendants=TRUE,
+ *     svn_mergeinfo_explicit, target_abspath, limit_path=NULL,
+ *     walked_path=NULL, ignore_invalid_mergeinfo=FALSE, ...)
+ *
+ *   except for the catalog keys being abspaths instead of repo-relpaths?
+ */
+static svn_error_t *
+get_wc_explicit_mergeinfo_catalog(apr_hash_t **subtrees_with_mergeinfo,
+                                  const char *target_abspath,
+                                  svn_depth_t depth,
+                                  svn_client_ctx_t *ctx,
+                                  apr_pool_t *result_pool,
+                                  apr_pool_t *scratch_pool)
+{
+  svn_opt_revision_t working_revision = { svn_opt_revision_working, { 0 } };
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  apr_hash_index_t *hi;
+
+  SVN_ERR(svn_client_propget4(subtrees_with_mergeinfo, SVN_PROP_MERGEINFO,
+                              target_abspath, &working_revision,
+                              &working_revision, NULL, depth, NULL,
+                              ctx, result_pool, scratch_pool));
+
+  /* Convert property values to svn_mergeinfo_t. */
+  for (hi = apr_hash_first(scratch_pool, *subtrees_with_mergeinfo);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *wc_path = svn__apr_hash_index_key(hi);
+      svn_string_t *mergeinfo_string = svn__apr_hash_index_val(hi);
+      svn_mergeinfo_t mergeinfo;
+      svn_error_t *err;
+
+      svn_pool_clear(iterpool);
+
+      err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_string->data,
+                                result_pool);
+      if (err)
+        {
+          if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
+            {
+              err = svn_error_createf(
+                SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
+                _("Invalid mergeinfo detected on '%s', "
+                  "mergetracking not possible"),
+                svn_dirent_local_style(wc_path, scratch_pool));
+            }
+          return svn_error_trace(err);
+        }
+      apr_hash_set(*subtrees_with_mergeinfo, wc_path, APR_HASH_KEY_STRING,
+                   mergeinfo);
+    }
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
 /* Helper for do_directory_merge() when performing merge-tracking aware
    merges.
 
@@ -5655,16 +5676,12 @@ get_mergeinfo_paths(apr_array_header_t *
   apr_hash_t *shallow_subtrees;
   apr_hash_t *missing_subtrees;
   struct pre_merge_status_baton_t pre_merge_status_baton;
-  svn_opt_revision_t working_revision = { svn_opt_revision_working, { 0 } };
 
   /* Case 1: Subtrees with explicit mergeinfo. */
-  SVN_ERR(svn_client_propget4(&subtrees_with_mergeinfo, SVN_PROP_MERGEINFO,
-                              merge_cmd_baton->target_abspath,
-                              &working_revision,
-                              &working_revision, NULL, depth, NULL,
-                              merge_cmd_baton->ctx, scratch_pool,
-                              scratch_pool));
-
+  SVN_ERR(get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
+                                            merge_cmd_baton->target_abspath,
+                                            depth, merge_cmd_baton->ctx,
+                                            result_pool, scratch_pool));
   if (subtrees_with_mergeinfo)
     {
       apr_hash_index_t *hi;
@@ -5673,29 +5690,15 @@ get_mergeinfo_paths(apr_array_header_t *
            hi;
            hi = apr_hash_next(hi))
         {
-          svn_error_t *err;
           const char *wc_path = svn__apr_hash_index_key(hi);
-          svn_string_t *mergeinfo_string = svn__apr_hash_index_val(hi);
+          svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi);
           svn_client__merge_path_t *mergeinfo_child =
             svn_client__merge_path_create(wc_path, result_pool);
 
           svn_pool_clear(iterpool);
 
           /* Stash this child's pre-existing mergeinfo. */
-          err = svn_mergeinfo_parse(&mergeinfo_child->pre_merge_mergeinfo,
-                                    mergeinfo_string->data, result_pool);
-          if (err)
-            {
-              if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
-                {
-                  err = svn_error_createf(
-                    SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
-                    _("Invalid mergeinfo detected on '%s', "
-                      "mergetracking not possible"),
-                    svn_dirent_local_style(wc_path, scratch_pool));
-                }
-              return svn_error_trace(err);
-            }
+          mergeinfo_child->pre_merge_mergeinfo = mergeinfo;
 
           /* Note if this child has non-inheritable mergeinfo */
           mergeinfo_child->has_noninheritable
@@ -5972,7 +5975,11 @@ get_mergeinfo_paths(apr_array_header_t *
          so it has explicit mergeinfo that reflects only CHILD's
          inheritable mergeinfo. */
 
-      if (child->has_noninheritable)
+      /* If depth is immediates or files then don't add new children if
+         CHILD is a subtree of the merge target; those children are below
+         the operational depth of the merge. */
+      if (child->has_noninheritable
+          && (i == 0 || depth == svn_depth_infinity))
         {
           const apr_array_header_t *children;
           int j;
@@ -6020,11 +6027,10 @@ get_mergeinfo_paths(apr_array_header_t *
                   if (!merge_cmd_baton->dry_run
                       && merge_cmd_baton->same_repos)
                     {
-                      svn_boolean_t inherited;
                       svn_mergeinfo_t mergeinfo;
 
                       SVN_ERR(svn_client__get_wc_mergeinfo(
-                        &mergeinfo, &inherited,
+                        &mergeinfo, NULL,
                         svn_mergeinfo_nearest_ancestor,
                         child_of_noninheritable->abspath,
                         merge_cmd_baton->target_abspath, NULL, FALSE,
@@ -6067,6 +6073,32 @@ log_changed_revs(void *baton,
 }
 
 
+/* Set *MIN_REV_P to the oldest and *MAX_REV_P to the youngest start or end
+ * revision occurring in RANGELIST, or to SVN_INVALID_REVNUM if RANGELIST
+ * is empty. */
+static void
+merge_range_find_extremes(svn_revnum_t *min_rev_p,
+                          svn_revnum_t *max_rev_p,
+                          const apr_array_header_t *rangelist)
+{
+  int i;
+
+  *min_rev_p = SVN_INVALID_REVNUM;
+  *max_rev_p = SVN_INVALID_REVNUM;
+  for (i = 0; i < rangelist->nelts; i++)
+    {
+      svn_merge_range_t *range
+        = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
+      svn_revnum_t range_min = MIN(range->start, range->end);
+      svn_revnum_t range_max = MAX(range->start, range->end);
+
+      if ((! SVN_IS_VALID_REVNUM(*min_rev_p)) || (range_min < *min_rev_p))
+        *min_rev_p = range_min;
+      if ((! SVN_IS_VALID_REVNUM(*max_rev_p)) || (range_max > *max_rev_p))
+        *max_rev_p = range_max;
+    }
+}
+
 /* Set *OPERATIVE_RANGES_P to an array of svn_merge_range_t * merge
    range objects copied wholesale from RANGES which have the property
    that in some revision within that range the object identified by
@@ -6086,8 +6118,7 @@ remove_noop_merge_ranges(apr_array_heade
                          apr_pool_t *pool)
 {
   int i;
-  svn_revnum_t oldest_rev = SVN_INVALID_REVNUM;
-  svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
+  svn_revnum_t oldest_rev, youngest_rev;
   apr_array_header_t *changed_revs =
     apr_array_make(pool, ranges->nelts, sizeof(svn_revnum_t));
   apr_array_header_t *operative_ranges =
@@ -6097,17 +6128,9 @@ remove_noop_merge_ranges(apr_array_heade
   APR_ARRAY_PUSH(log_targets, const char *) = "";
 
   /* Find the revision extremes of the RANGES we have. */
-  for (i = 0; i < ranges->nelts; i++)
-    {
-      svn_merge_range_t *r = APR_ARRAY_IDX(ranges, i, svn_merge_range_t *);
-      svn_revnum_t max_rev = MAX(r->start, r->end);
-      svn_revnum_t min_rev = MIN(r->start, r->end) + 1;
-
-      if ((! SVN_IS_VALID_REVNUM(youngest_rev)) || (max_rev > youngest_rev))
-        youngest_rev = max_rev;
-      if ((! SVN_IS_VALID_REVNUM(oldest_rev)) || (min_rev < oldest_rev))
-        oldest_rev = min_rev;
-    }
+  merge_range_find_extremes(&oldest_rev, &youngest_rev, ranges);
+  if (SVN_IS_VALID_REVNUM(oldest_rev))
+    oldest_rev++;  /* make it inclusive */
 
   /* Get logs across those ranges, recording which revisions hold
      changes to our object's history. */
@@ -6293,18 +6316,17 @@ combine_range_with_segments(apr_array_he
  * PEG_REVNUM instead of PEG_REVISION. */
 static svn_error_t *
 normalize_merge_sources_internal(apr_array_header_t **merge_sources_p,
-                        const char *source_url,
-                        const char *source_root_url,
-                        svn_revnum_t peg_revnum,
-                        const apr_array_header_t *merge_range_ts,
-                        svn_ra_session_t *ra_session,
-                        svn_client_ctx_t *ctx,
-                        apr_pool_t *result_pool,
-                        apr_pool_t *scratch_pool)
+                                 const char *source_url,
+                                 svn_revnum_t peg_revnum,
+                                 const apr_array_header_t *merge_range_ts,
+                                 svn_ra_session_t *ra_session,
+                                 svn_client_ctx_t *ctx,
+                                 apr_pool_t *result_pool,
+                                 apr_pool_t *scratch_pool)
 {
-  svn_revnum_t oldest_requested = SVN_INVALID_REVNUM;
-  svn_revnum_t youngest_requested = SVN_INVALID_REVNUM;
+  svn_revnum_t oldest_requested, youngest_requested;
   svn_revnum_t trim_revision = SVN_INVALID_REVNUM;
+  const char *source_root_url;
   apr_array_header_t *segments;
   int i;
 
@@ -6316,22 +6338,8 @@ normalize_merge_sources_internal(apr_arr
     return SVN_NO_ERROR;
 
   /* Find the extremes of the revisions across our set of ranges. */
-  for (i = 0; i < merge_range_ts->nelts; i++)
-    {
-      svn_merge_range_t *range =
-        APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *);
-      svn_revnum_t minrev = MIN(range->start, range->end);
-      svn_revnum_t maxrev = MAX(range->start, range->end);
-
-      /* Keep a running tally of the oldest and youngest requested
-         revisions. */
-      if ((! SVN_IS_VALID_REVNUM(oldest_requested))
-          || (minrev < oldest_requested))
-        oldest_requested = minrev;
-      if ((! SVN_IS_VALID_REVNUM(youngest_requested))
-          || (maxrev > youngest_requested))
-        youngest_requested = maxrev;
-    }
+  merge_range_find_extremes(&oldest_requested, &youngest_requested,
+                            merge_range_ts);
 
   /* ### FIXME:  Our underlying APIs can't yet handle the case where
      the peg revision isn't the youngest of the three revisions.  So
@@ -6357,6 +6365,8 @@ normalize_merge_sources_internal(apr_arr
                                               oldest_requested,
                                               ctx, result_pool));
 
+  SVN_ERR(svn_ra_get_repos_root2(ra_session, &source_root_url, scratch_pool));
+
   /* See if we fetched enough history to do the job.  "Surely we did,"
      you say.  "After all, we covered the entire requested merge
      range."  Yes, that's true, but if our first segment doesn't
@@ -6421,12 +6431,7 @@ normalize_merge_sources_internal(apr_arr
                   new_segment->range_start = copyfrom_rev;
                   new_segment->range_end = copyfrom_rev;
                   segment->range_start = copyfrom_rev + 1;
-                  APR_ARRAY_PUSH(segments, svn_location_segment_t *) = NULL;
-                  memmove(segments->elts + segments->elt_size,
-                          segments->elts,
-                          segments->elt_size * (segments->nelts - 1));
-                  APR_ARRAY_IDX(segments, 0, svn_location_segment_t *) =
-                    new_segment;
+                  svn_sort__array_insert(&new_segment, segments, 0);
                 }
             }
         }
@@ -6439,7 +6444,6 @@ normalize_merge_sources_internal(apr_arr
       svn_merge_range_t *range =
         APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *);
       apr_array_header_t *merge_sources;
-      int j;
 
       if (SVN_IS_VALID_REVNUM(trim_revision))
         {
@@ -6461,11 +6465,7 @@ normalize_merge_sources_internal(apr_arr
       SVN_ERR(combine_range_with_segments(&merge_sources, range,
                                           segments, source_root_url,
                                           result_pool));
-      for (j = 0; j < merge_sources->nelts; j++)
-        {
-          APR_ARRAY_PUSH(*merge_sources_p, merge_source_t *) =
-            APR_ARRAY_IDX(merge_sources, j, merge_source_t *);
-        }
+      apr_array_cat(*merge_sources_p, merge_sources);
     }
 
   return SVN_NO_ERROR;
@@ -6485,8 +6485,6 @@ normalize_merge_sources_internal(apr_arr
 
    If PEG_REVISION is unspecified, treat that it as HEAD.
 
-   SOURCE_ROOT_URL is the root URL of the source repository.
-
    Use RA_SESSION -- whose session URL matches SOURCE_URL -- to answer
    historical questions.
 
@@ -6502,7 +6500,6 @@ static svn_error_t *
 normalize_merge_sources(apr_array_header_t **merge_sources_p,
                         const char *source,
                         const char *source_url,
-                        const char *source_root_url,
                         const svn_opt_revision_t *peg_revision,
                         const apr_array_header_t *ranges_to_merge,
                         svn_ra_session_t *ra_session,
@@ -6572,7 +6569,7 @@ normalize_merge_sources(apr_array_header
     }
 
   SVN_ERR(normalize_merge_sources_internal(
-            merge_sources_p, source_url, source_root_url, peg_revnum,
+            merge_sources_p, source_url, peg_revnum,
             merge_range_ts, ra_session, ctx, result_pool, scratch_pool));
 
   svn_pool_destroy(iterpool);
@@ -6670,14 +6667,7 @@ do_file_merge(svn_mergeinfo_catalog_t re
               merge_cmd_baton_t *merge_b,
               apr_pool_t *scratch_pool)
 {
-  apr_hash_t *props1, *props2;
-  const char *tmpfile1, *tmpfile2;
-  const char *mimetype1, *mimetype2;
-  svn_string_t *pval;
-  apr_array_header_t *propchanges, *remaining_ranges;
-  svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
-  svn_wc_notify_state_t text_state = svn_wc_notify_state_unknown;
-  svn_boolean_t tree_conflicted = FALSE;
+  apr_array_header_t *remaining_ranges;
   svn_client_ctx_t *ctx = merge_b->ctx;
   const char *mergeinfo_path;
   svn_merge_range_t range;
@@ -6794,15 +6784,18 @@ do_file_merge(svn_mergeinfo_catalog_t re
 
       for (i = 0; i < ranges_to_merge->nelts; i++)
         {
+          svn_merge_range_t *r = APR_ARRAY_IDX(ranges_to_merge, i,
+                                               svn_merge_range_t *);
           svn_wc_notify_t *n;
           svn_boolean_t header_sent = FALSE;
           svn_ra_session_t *ra_session1, *ra_session2;
-
-          /* When using this merge range, account for the exclusivity of
-             its low value (which is indicated by this operation being a
-             merge vs. revert). */
-          svn_merge_range_t *r = APR_ARRAY_IDX(ranges_to_merge, i,
-                                               svn_merge_range_t *);
+          const char *tmpfile1, *tmpfile2;
+          apr_hash_t *props1, *props2;
+          svn_string_t *pval;
+          const char *mimetype1, *mimetype2;
+          apr_array_header_t *propchanges;
+          svn_wc_notify_state_t prop_state, text_state;
+          svn_boolean_t tree_conflicted = TRUE;
 
           svn_pool_clear(iterpool);
 
@@ -6865,33 +6858,27 @@ do_file_merge(svn_mergeinfo_catalog_t re
               SVN_ERR(merge_file_deleted(&text_state,
                                          &tree_conflicted,
                                          target_relpath,
-                                         tmpfile1,
-                                         tmpfile2,
+                                         tmpfile1, tmpfile2,
                                          mimetype1, mimetype2,
                                          props1,
-                                         merge_b,
-                                         iterpool));
+                                         merge_b, iterpool));
               single_file_merge_notify(notify_b, target_relpath,
                                        tree_conflicted
                                          ? svn_wc_notify_tree_conflict
                                          : svn_wc_notify_update_delete,
-                                       text_state,
-                                       svn_wc_notify_state_unknown,
+                                       text_state, svn_wc_notify_state_unknown,
                                        n, &header_sent, iterpool);
 
               /* ...plus add... */
               SVN_ERR(merge_file_added(&text_state, &prop_state,
                                        &tree_conflicted,
                                        target_relpath,
-                                       tmpfile1,
-                                       tmpfile2,
-                                       r->start,
-                                       r->end,
+                                       tmpfile1, tmpfile2,
+                                       r->start, r->end,
                                        mimetype1, mimetype2,
                                        NULL, SVN_INVALID_REVNUM,
                                        propchanges, props1,
-                                       merge_b,
-                                       iterpool));
+                                       merge_b, iterpool));
               single_file_merge_notify(notify_b, target_relpath,
                                        tree_conflicted
                                          ? svn_wc_notify_tree_conflict
@@ -6905,14 +6892,11 @@ do_file_merge(svn_mergeinfo_catalog_t re
               SVN_ERR(merge_file_changed(&text_state, &prop_state,
                                          &tree_conflicted,
                                          target_relpath,
-                                         tmpfile1,
-                                         tmpfile2,
-                                         r->start,
-                                         r->end,
+                                         tmpfile1, tmpfile2,
+                                         r->start, r->end,
                                          mimetype1, mimetype2,
                                          propchanges, props1,
-                                         merge_b,
-                                         iterpool));
+                                         merge_b, iterpool));
               single_file_merge_notify(notify_b, target_relpath,
                                        tree_conflicted
                                          ? svn_wc_notify_tree_conflict
@@ -7019,11 +7003,11 @@ do_file_merge(svn_mergeinfo_catalog_t re
    drive adds explicit mergeinfo to a path which didn't have any explicit
    mergeinfo previously.
 
-   MERGE_B and NOTIFY_B are cascaded from the arguments of the same
+   MERGE_B is cascaded from the argument of the same
    name in do_directory_merge().  Should be called only after
    do_directory_merge() has called populate_remaining_ranges() and populated
    the remaining_ranges field of each child in
-   NOTIFY_B->CHILDREN_WITH_MERGEINFO (i.e. the remaining_ranges fields can be
+   CHILDREN_WITH_MERGEINFO (i.e. the remaining_ranges fields can be
    empty but never NULL).
 
    If MERGE_B->DRY_RUN is true do nothing, if it is false then
@@ -7031,17 +7015,17 @@ do_file_merge(svn_mergeinfo_catalog_t re
    path's inherited mergeinfo (if any) with its working explicit mergeinfo
    and set that as the path's new explicit mergeinfo.  Then add an
    svn_client__merge_path_t * element representing the path to
-   NOTIFY_B->CHILDREN_WITH_MERGEINFO if it isn't already present.  All fields
-   in any elements added to NOTIFY_B->CHILDREN_WITH_MERGEINFO are initialized
+   CHILDREN_WITH_MERGEINFO if it isn't already present.  All fields
+   in any elements added to CHILDREN_WITH_MERGEINFO are initialized
    to FALSE/NULL with the exception of 'path' and 'remaining_ranges'.  The
    latter is set to a rangelist equal to the remaining_ranges of the path's
-   nearest path-wise ancestor in NOTIFY_B->CHILDREN_WITH_MERGEINFO.
+   nearest path-wise ancestor in CHILDREN_WITH_MERGEINFO.
 
-   Any elements added to NOTIFY_B->CHILDREN_WITH_MERGEINFO are allocated
+   Any elements added to CHILDREN_WITH_MERGEINFO are allocated
    in POOL. */
 static svn_error_t *
 process_children_with_new_mergeinfo(merge_cmd_baton_t *merge_b,
-                                    notification_receiver_baton_t *notify_b,
+                                    apr_array_header_t *children_with_mergeinfo,
                                     apr_pool_t *pool)
 {
   apr_pool_t *iterpool;
@@ -7061,7 +7045,6 @@ process_children_with_new_mergeinfo(merg
       const char *path_url;
       svn_mergeinfo_t path_inherited_mergeinfo;
       svn_mergeinfo_t path_explicit_mergeinfo;
-      svn_boolean_t inherited;
       svn_client__merge_path_t *new_child;
 
       apr_pool_clear(iterpool);
@@ -7070,8 +7053,7 @@ process_children_with_new_mergeinfo(merg
                                    pool, pool));
 
       /* Get the path's new explicit mergeinfo... */
-      SVN_ERR(svn_client__get_wc_mergeinfo(&path_explicit_mergeinfo,
-                                           &inherited,
+      SVN_ERR(svn_client__get_wc_mergeinfo(&path_explicit_mergeinfo, NULL,
                                            svn_mergeinfo_explicit,
                                            abspath_with_new_mergeinfo,
                                            NULL, NULL, FALSE,
@@ -7091,7 +7073,7 @@ process_children_with_new_mergeinfo(merg
              the merge. */
           SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(
             &path_inherited_mergeinfo,
-            &inherited, NULL,
+            NULL, NULL,
             FALSE,
             svn_mergeinfo_nearest_ancestor, /* We only want inherited MI */
             merge_b->ra_session2,
@@ -7113,19 +7095,17 @@ process_children_with_new_mergeinfo(merg
                                           FALSE, merge_b->ctx, iterpool));
             }
 
-          /* If the path is not in NOTIFY_B->CHILDREN_WITH_MERGEINFO
-             then add it. */
+          /* If the path is not in CHILDREN_WITH_MERGEINFO then add it. */
           new_child =
-            get_child_with_mergeinfo(notify_b->children_with_mergeinfo,
+            get_child_with_mergeinfo(children_with_mergeinfo,
                                      abspath_with_new_mergeinfo);
           if (!new_child)
             {
               int parent_index =
-                find_nearest_ancestor(notify_b->children_with_mergeinfo,
+                find_nearest_ancestor(children_with_mergeinfo,
                                       FALSE, abspath_with_new_mergeinfo);
               svn_client__merge_path_t *parent =
-                APR_ARRAY_IDX(notify_b->children_with_mergeinfo,
-                              parent_index,
+                APR_ARRAY_IDX(children_with_mergeinfo, parent_index,
                               svn_client__merge_path_t *);
               new_child
                 = svn_client__merge_path_create(abspath_with_new_mergeinfo,
@@ -7133,10 +7113,10 @@ process_children_with_new_mergeinfo(merg
 
               /* If path_with_new_mergeinfo is the merge target itself
                  then it should already be in
-                 NOTIFY_B->CHILDREN_WITH_MERGEINFO per the criteria of
+                 CHILDREN_WITH_MERGEINFO per the criteria of
                  get_mergeinfo_paths() and we shouldn't be in this block.
                  If path_with_new_mergeinfo is a subtree then it must have
-                 a parent in NOTIFY_B->CHILDREN_WITH_MERGEINFO if only
+                 a parent in CHILDREN_WITH_MERGEINFO if only
                  the merge target itself...so if we don't find a parent
                  the caller has done something quite wrong. */
               SVN_ERR_ASSERT(parent);
@@ -7145,8 +7125,7 @@ process_children_with_new_mergeinfo(merg
               /* Set the path's remaining_ranges equal to its parent's. */
               new_child->remaining_ranges = svn_rangelist_dup(
                  parent->remaining_ranges, pool);
-              insert_child_to_merge(notify_b->children_with_mergeinfo,
-                                    new_child, pool);
+              insert_child_to_merge(children_with_mergeinfo, new_child, pool);
             }
         }
       /* Return MERGE_B->RA_SESSION2 to its initial state if we
@@ -7185,10 +7164,9 @@ path_is_subtree(const char *local_abspat
   return FALSE;
 }
 
-/* Return true if LOCAL_ABSPATH is equal to, or is a subtree of, any of the
-   paths in NOTIFY_B->MERGED_PATHS, NOTIFY_B->SKIPPED_PATHS,
-   NOTIFY_B->ADDED_PATHS, or NOTIFY_B->CONFLICTED_PATHS.  Return false
-   otherwise. */
+/* Return true if any path in NOTIFY_B->MERGED_PATHS, NOTIFY_B->SKIPPED_PATHS,
+   NOTIFY_B->ADDED_PATHS, or NOTIFY_B->CONFLICTED_PATHS is equal to, or is a
+   subtree of LOCAL_ABSPATH.  Return false otherwise. */
 static svn_boolean_t
 subtree_touched_by_merge(const char *local_abspath,
                          notification_receiver_baton_t *notify_b,
@@ -7210,8 +7188,8 @@ subtree_touched_by_merge(const char *loc
    are all cascaded from do_directory_merge's arguments of the same names.
 
    NOTE: This is a very thin wrapper around drive_merge_report_editor() and
-   exists only to populate NOTIFY_B->CHILREN_WITH_MERGEINFO with the single
-   element expected during mergeinfo unaware merges..
+   exists only to populate NOTIFY_B->CHILDREN_WITH_MERGEINFO with the single
+   element expected during mergeinfo unaware merges.
 */
 static svn_error_t *
 do_mergeinfo_unaware_dir_merge(const char *url1,
@@ -7405,15 +7383,8 @@ record_mergeinfo_for_dir_merge(svn_merge
 
   /* Regardless of what subtrees in MERGE_B->TARGET_ABSPATH might be missing
      could this merge have been operative? */
-  if ((notify_b->merged_abspaths
-       && apr_hash_count(notify_b->merged_abspaths))
-      || (notify_b->skipped_abspaths
-          && apr_hash_count(notify_b->skipped_abspaths))
-      || (notify_b->added_abspaths
-          && apr_hash_count(notify_b->added_abspaths))
-      || (notify_b->tree_conflicted_abspaths
-          && apr_hash_count(notify_b->tree_conflicted_abspaths)))
-    operative_merge = TRUE;
+  operative_merge = subtree_touched_by_merge(merge_b->target_abspath,
+                                             notify_b, iterpool);
 
   /* If this couldn't be an operative merge then don't bother with
      the added complexity (and user confusion) of non-inheritable ranges.
@@ -7425,7 +7396,7 @@ record_mergeinfo_for_dir_merge(svn_merge
      NOTIFY_B->CHILDREN_WITH_MERGEINFO
      before we calculate the merges performed. */
   remove_absent_children(merge_b->target_abspath,
-                         notify_b->children_with_mergeinfo, notify_b);
+                         notify_b->children_with_mergeinfo);
 
 
   /* Find all issue #3642 children (i.e immediate child directories of the
@@ -7732,11 +7703,11 @@ record_mergeinfo_for_dir_merge(svn_merge
 
    Record mergeinfo describing a merge of
    MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
-   MERGEINFO_FSPATH to each path in NOTIFY_B->ADDED_ABSPATHS which has explicit
+   MERGEINFO_FSPATH to each path in ADDED_ABSPATHS which has explicit
    mergeinfo or is the immediate child of a parent with explicit
    non-inheritable mergeinfo.
 
-   DEPTH, NOTIFY_B, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS, are
+   DEPTH, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS, are
    cascaded from do_directory_merge's arguments of the same names.
 
    Note: This is intended to support forward merges only, i.e.
@@ -7748,7 +7719,7 @@ record_mergeinfo_for_added_subtrees(
   const char *mergeinfo_fspath,
   svn_depth_t depth,
   svn_boolean_t squelch_mergeinfo_notifications,
-  notification_receiver_baton_t *notify_b,
+  apr_hash_t *added_abspaths,
   merge_cmd_baton_t *merge_b,
   apr_pool_t *pool)
 {
@@ -7756,26 +7727,24 @@ record_mergeinfo_for_added_subtrees(
   apr_hash_index_t *hi;
 
   /* If no paths were added by the merge then we have nothing to do. */
-  if (!notify_b->added_abspaths)
+  if (!added_abspaths)
     return SVN_NO_ERROR;
 
   SVN_ERR_ASSERT(merged_range->start < merged_range->end);
 
   iterpool = svn_pool_create(pool);
-  for (hi = apr_hash_first(pool, notify_b->added_abspaths); hi;
-       hi = apr_hash_next(hi))
+  for (hi = apr_hash_first(pool, added_abspaths); hi; hi = apr_hash_next(hi))
     {
       const char *added_abspath = svn__apr_hash_index_key(hi);
       const char *dir_abspath;
       svn_mergeinfo_t parent_mergeinfo;
       svn_mergeinfo_t added_path_mergeinfo;
-      svn_boolean_t inherited; /* used multiple times, but ignored */
 
       apr_pool_clear(iterpool);
       dir_abspath = svn_dirent_dirname(added_abspath, iterpool);
 
       /* Grab the added path's explicit mergeinfo. */
-      SVN_ERR(svn_client__get_wc_mergeinfo(&added_path_mergeinfo, &inherited,
+      SVN_ERR(svn_client__get_wc_mergeinfo(&added_path_mergeinfo, NULL,
                                            svn_mergeinfo_explicit,
                                            added_abspath, NULL, NULL, FALSE,
                                            merge_b->ctx, iterpool, iterpool));
@@ -7783,7 +7752,7 @@ record_mergeinfo_for_added_subtrees(
       /* If the added path doesn't have explicit mergeinfo, does its immediate
          parent have non-inheritable mergeinfo? */
       if (!added_path_mergeinfo)
-        SVN_ERR(svn_client__get_wc_mergeinfo(&parent_mergeinfo, &inherited,
+        SVN_ERR(svn_client__get_wc_mergeinfo(&parent_mergeinfo, NULL,
                                              svn_mergeinfo_explicit,
                                              dir_abspath, NULL, NULL, FALSE,
                                              merge_b->ctx,
@@ -7792,9 +7761,6 @@ record_mergeinfo_for_added_subtrees(
       if (added_path_mergeinfo
           || svn_mergeinfo__is_noninheritable(parent_mergeinfo, iterpool))
         {
-          svn_client__merge_path_t *target_merge_path =
-            APR_ARRAY_IDX(notify_b->children_with_mergeinfo, 0,
-                          svn_client__merge_path_t *);
           svn_node_kind_t added_path_kind;
           svn_mergeinfo_t merge_mergeinfo;
           svn_mergeinfo_t adds_history_as_mergeinfo;
@@ -7816,13 +7782,10 @@ record_mergeinfo_for_added_subtrees(
                                || depth == svn_depth_immediates))),
                         iterpool);
 
-          /* Create the new mergeinfo path for
-             added_path's mergeinfo. */
-
-          /* abs_added_path had better be a child of abs_target_path
-             or something is *really* wrong. */
-
-          rel_added_path = svn_dirent_is_child(target_merge_path->abspath,
+          /* Create the new mergeinfo path for added_path's mergeinfo.
+             (added_abspath had better be a child of target_abspath
+             or something is *really* wrong.) */
+          rel_added_path = svn_dirent_is_child(merge_b->target_abspath,
                                                added_abspath, iterpool);
           SVN_ERR_ASSERT(rel_added_path);
           added_path_mergeinfo_fspath = svn_fspath__join(mergeinfo_fspath,
@@ -8070,13 +8033,13 @@ log_noop_revs(void *baton,
 
 /* Helper for do_directory_merge().
 
-   URL1, REVISION1, URL2, REVISION2, NOTIFY_B, and MERGE_B are
+   URL1, REVISION1, URL2, REVISION2, and MERGE_B are
    cascaded from the arguments of the same name in do_directory_merge().
    RA_SESSION is the session for URL2@REVISION2.
 
    Find all the ranges required by subtrees in
-   NOTIFY_B->CHILDREN_WITH_MERGEINFO that are *not* required by
-   MERGE_B->TARGET_ABSPATH (i.e. NOTIFY_B->CHILDREN_WITH_MERGEINFO[0]).  If such
+   CHILDREN_WITH_MERGEINFO that are *not* required by
+   MERGE_B->TARGET_ABSPATH (i.e. CHILDREN_WITH_MERGEINFO[0]).  If such
    ranges exist, then find any subset of ranges which, if merged, would be
    inoperative.  Finally, if any inoperative ranges are found then remove
    these ranges from all of the subtree's REMAINING_RANGES.
@@ -8090,7 +8053,7 @@ remove_noop_subtree_ranges(const char *u
                            const char *url2,
                            svn_revnum_t revision2,
                            svn_ra_session_t *ra_session,
-                           notification_receiver_baton_t *notify_b,
+                           apr_array_header_t *children_with_mergeinfo,
                            merge_cmd_baton_t *merge_b,
                            apr_pool_t *result_pool,
                            apr_pool_t *scratch_pool)
@@ -8098,8 +8061,7 @@ remove_noop_subtree_ranges(const char *u
   /* ### Do we need to check that we are at a uniform working revision? */
   int i;
   svn_client__merge_path_t *root_child =
-    APR_ARRAY_IDX(notify_b->children_with_mergeinfo, 0,
-                  svn_client__merge_path_t *);
+    APR_ARRAY_IDX(children_with_mergeinfo, 0, svn_client__merge_path_t *);
   apr_array_header_t *requested_ranges;
   apr_array_header_t *subtree_gap_ranges;
   apr_array_header_t *subtree_remaining_ranges;
@@ -8108,7 +8070,6 @@ remove_noop_subtree_ranges(const char *u
   svn_merge_range_t *oldest_gap_rev;
   svn_merge_range_t *youngest_gap_rev;
   apr_array_header_t *inoperative_ranges;
-  const char *repos_root_url;
   apr_pool_t *iterpool;
 
 
@@ -8117,7 +8078,7 @@ remove_noop_subtree_ranges(const char *u
     return SVN_NO_ERROR;
 
   /* Another easy out: There are no subtrees. */
-  if (notify_b->children_with_mergeinfo->nelts < 2)
+  if (children_with_mergeinfo->nelts < 2)
     return SVN_NO_ERROR;
 
   subtree_remaining_ranges = apr_array_make(scratch_pool, 1,
@@ -8139,11 +8100,10 @@ remove_noop_subtree_ranges(const char *u
 
   /* Create a rangelist describing every range required across all subtrees. */
   iterpool = svn_pool_create(scratch_pool);
-  for (i = 1; i < notify_b->children_with_mergeinfo->nelts; i++)
+  for (i = 1; i < children_with_mergeinfo->nelts; i++)
     {
       svn_client__merge_path_t *child =
-        APR_ARRAY_IDX(notify_b->children_with_mergeinfo, i,
-                      svn_client__merge_path_t *);
+        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
 
       svn_pool_clear(iterpool);
 
@@ -8176,21 +8136,17 @@ remove_noop_subtree_ranges(const char *u
   youngest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges,
                          subtree_gap_ranges->nelts - 1, svn_merge_range_t *);
 
-  SVN_ERR(svn_wc__node_get_repos_info(&repos_root_url, NULL,
-                                      merge_b->ctx->wc_ctx,
-                                      merge_b->target_abspath,
-                                      scratch_pool, scratch_pool));
-
   /* Set up the log baton. */
   log_gap_baton.merge_b = merge_b;
-  log_gap_baton.children_with_mergeinfo = notify_b->children_with_mergeinfo;
+  log_gap_baton.children_with_mergeinfo = children_with_mergeinfo;
   SVN_ERR(svn_client__path_relative_to_root(
                     &(log_gap_baton.target_fspath), merge_b->ctx->wc_ctx,
-                    merge_b->target_abspath, repos_root_url, TRUE, NULL,
+                    merge_b->target_abspath,
+                    merge_b->repos_root_url, TRUE, NULL,
                     result_pool, scratch_pool));
   SVN_ERR(svn_client__path_relative_to_root(
                     &(log_gap_baton.source_fspath), merge_b->ctx->wc_ctx,
-                    url2, repos_root_url, TRUE, NULL,
+                    url2, merge_b->repos_root_url, TRUE, NULL,
                     result_pool, scratch_pool));
   log_gap_baton.merged_ranges = apr_array_make(scratch_pool, 0,
                                                sizeof(svn_revnum_t *));
@@ -8221,11 +8177,10 @@ remove_noop_subtree_ranges(const char *u
   SVN_ERR(svn_rangelist_merge2(log_gap_baton.merged_ranges, inoperative_ranges,
                                scratch_pool, scratch_pool));
 
-  for (i = 1; i < notify_b->children_with_mergeinfo->nelts; i++)
+  for (i = 1; i < children_with_mergeinfo->nelts; i++)
     {
       svn_client__merge_path_t *child =
-        APR_ARRAY_IDX(notify_b->children_with_mergeinfo, i,
-                      svn_client__merge_path_t *);
+        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
 
       /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
       if (child->remaining_ranges && child->remaining_ranges->nelts)
@@ -8389,8 +8344,8 @@ do_directory_merge(svn_mergeinfo_catalog
          We'll do this twice, right now for the start of the mergeinfo we will
          ultimately record to describe this merge and then later for the
          start of the actual editor drive. */
-      new_range_start = get_most_inclusive_start_rev(
-        notify_b->children_with_mergeinfo, is_rollback);
+      new_range_start = get_most_inclusive_rev(
+        notify_b->children_with_mergeinfo, is_rollback, TRUE);
       if (SVN_IS_VALID_REVNUM(new_range_start))
         range.start = new_range_start;
 
@@ -8399,30 +8354,31 @@ do_directory_merge(svn_mergeinfo_catalog
       if (!is_rollback)
         SVN_ERR(remove_noop_subtree_ranges(url1, revision1, url2, revision2,
                                            ra_session,
-                                           notify_b, merge_b,
-                                           scratch_pool, iterpool));
+                                           notify_b->children_with_mergeinfo,
+                                           merge_b, scratch_pool, iterpool));
 
       /* Adjust subtrees' remaining_ranges to deal with issue #3067 */
       SVN_ERR(fix_deleted_subtree_ranges(url1, revision1, url2, revision2,
-                                         ra_session, notify_b, merge_b,
-                                         scratch_pool, iterpool));
+                                         ra_session,
+                                         notify_b->children_with_mergeinfo,
+                                         merge_b, scratch_pool, iterpool));
 
       /* remove_noop_subtree_ranges() and/or fix_deleted_subtree_range()
          may have further refined the starting revision for our editor
          drive. */
       start_rev =
-        get_most_inclusive_start_rev(notify_b->children_with_mergeinfo,
-                                     is_rollback);
+        get_most_inclusive_rev(notify_b->children_with_mergeinfo,
+                               is_rollback, TRUE);
 
       /* Is there anything to merge? */
       if (SVN_IS_VALID_REVNUM(start_rev))
         {
-          /* Now examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the youngest
+          /* Now examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the oldest
              ending revision that actually needs to be merged (for reverse
-             merges this is the oldest starting revision). */
+             merges this is the youngest ending revision). */
            svn_revnum_t end_rev =
-             get_most_inclusive_end_rev(notify_b->children_with_mergeinfo,
-                                        is_rollback);
+             get_most_inclusive_rev(notify_b->children_with_mergeinfo,
+                                    is_rollback, FALSE);
 
           /* While END_REV is valid, do the following:
 
@@ -8456,8 +8412,10 @@ do_directory_merge(svn_mergeinfo_catalog
               svn_revnum_t next_end_rev;
               const char *real_url1 = url1, *real_url2 = url2;
               const char *old_sess1_url = NULL, *old_sess2_url = NULL;
-              svn_merge_range_t *first_target_range = APR_ARRAY_IDX(
-                target_merge_path->remaining_ranges, 0, svn_merge_range_t *);
+              svn_merge_range_t *first_target_range
+                = (target_merge_path->remaining_ranges->nelts == 0 ? NULL
+                   : APR_ARRAY_IDX(target_merge_path->remaining_ranges, 0,
+                                   svn_merge_range_t *));
 
               /* Issue #3324: Stop editor abuse!  Don't call
                  drive_merge_report_editor() in such a way that we request an
@@ -8545,24 +8503,26 @@ do_directory_merge(svn_mergeinfo_catalog
               /* If any paths picked up explicit mergeinfo as a result of
                  the merge we need to make sure any mergeinfo those paths
                  inherited is recorded and then add these paths to
-                 MERGE_B->CHILDREN_WITH_MERGEINFO.*/
-              SVN_ERR(process_children_with_new_mergeinfo(merge_b, notify_b,
-                                                          scratch_pool));
+                 NOTIFY_B->CHILDREN_WITH_MERGEINFO.*/
+              SVN_ERR(process_children_with_new_mergeinfo(
+                        merge_b, notify_b->children_with_mergeinfo,
+                        scratch_pool));
 
               /* If any subtrees had their explicit mergeinfo deleted as a
                  result of the merge then remove these paths from
-                 MERGE_B->CHILDREN_WITH_MERGEINFO since there is no need
+                 NOTIFY_B->CHILDREN_WITH_MERGEINFO since there is no need
                  to consider these subtrees for subsequent editor drives
                  nor do we want to record mergeinfo on them describing
                  the merge itself. */
-              remove_children_with_deleted_mergeinfo(merge_b, notify_b);
+              remove_children_with_deleted_mergeinfo(
+                merge_b, notify_b->children_with_mergeinfo);
 
               /* Prepare for the next iteration (if any). */
               remove_first_range_from_remaining_ranges(
                 end_rev, notify_b->children_with_mergeinfo, scratch_pool);
               next_end_rev =
-                get_most_inclusive_end_rev(notify_b->children_with_mergeinfo,
-                                           is_rollback);
+                get_most_inclusive_rev(notify_b->children_with_mergeinfo,
+                                       is_rollback, FALSE);
               if ((next_end_rev != SVN_INVALID_REVNUM || abort_on_conflicts)
                   && is_path_conflicted_by_merge(merge_b))
                 {
@@ -8577,8 +8537,8 @@ do_directory_merge(svn_mergeinfo_catalog
                   break;
                 }
               start_rev =
-                get_most_inclusive_start_rev(notify_b->children_with_mergeinfo,
-                                             is_rollback);
+                get_most_inclusive_rev(notify_b->children_with_mergeinfo,
+                                       is_rollback, TRUE);
               end_rev = next_end_rev;
             }
         }
@@ -8635,7 +8595,7 @@ do_directory_merge(svn_mergeinfo_catalog
           err = record_mergeinfo_for_added_subtrees(
                   &range, mergeinfo_path, depth,
                   squelch_mergeinfo_notifications,
-                  notify_b, merge_b, scratch_pool);
+                  notify_b->added_abspaths, merge_b, scratch_pool);
         }
     }
 
@@ -8981,8 +8941,8 @@ do_merge(apr_hash_t **modified_subtrees,
 
    The merge is between URL1@REV1 (in URL1_RA_SESSION1) and URL2@REV2 (in
    URL2_RA_SESSION2); YC_REV is their youngest common ancestor.
-   SOURCE_REPOS_ROOT and WC_REPOS_ROOT are the repository roots of the
-   source URL and the target working copy.  Other arguments are as in
+   SAME_REPOS must be true if and only if the source URLs are in the same
+   repository as the target working copy.  Other arguments are as in
    all of the public merge APIs.
 
    *USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
@@ -8999,8 +8959,7 @@ merge_cousins_and_supplement_mergeinfo(c
                                        const char *URL2,
                                        svn_revnum_t rev2,
                                        svn_revnum_t yc_rev,
-                                       url_uuid_t *source_repos_root,
-                                       url_uuid_t *wc_repos_root,
+                                       svn_boolean_t same_repos,
                                        svn_depth_t depth,
                                        svn_boolean_t ignore_ancestry,
                                        svn_boolean_t force,
@@ -9012,7 +8971,6 @@ merge_cousins_and_supplement_mergeinfo(c
                                        apr_pool_t *scratch_pool)
 {
   apr_array_header_t *remove_sources, *add_sources;
-  svn_boolean_t same_repos;
   apr_hash_t *modified_subtrees = NULL;
 
   /* Sure we could use SCRATCH_POOL througout this function, but since this
@@ -9023,16 +8981,13 @@ merge_cousins_and_supplement_mergeinfo(c
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
 
-  same_repos = is_same_repos(source_repos_root, wc_repos_root,
-                             TRUE /* strict_urls */);
-
   SVN_ERR(normalize_merge_sources_internal(
-            &remove_sources, URL1, source_repos_root->url, rev1,
+            &remove_sources, URL1, rev1,
             svn_rangelist__initialize(rev1, yc_rev, TRUE, scratch_pool),
             URL1_ra_session, ctx, scratch_pool, subpool));
 
   SVN_ERR(normalize_merge_sources_internal(
-            &add_sources, URL2, source_repos_root->url, rev2,
+            &add_sources, URL2, rev2,
             svn_rangelist__initialize(yc_rev, rev2, TRUE, scratch_pool),
             URL2_ra_session, ctx, scratch_pool, subpool));
 
@@ -9097,42 +9052,8 @@ merge_cousins_and_supplement_mergeinfo(c
       SVN_ERR(svn_mergeinfo_catalog_merge(add_result_catalog,
                                           remove_result_catalog,
                                           scratch_pool, scratch_pool));
-
-      if (apr_hash_count(add_result_catalog))
-        {
-          int i;
-          apr_array_header_t *sorted_cat =
-            svn_sort__hash(add_result_catalog,
-                           svn_sort_compare_items_as_paths,
-                           scratch_pool);
-          for (i = 0; i < sorted_cat->nelts; i++)
-            {
-              svn_sort__item_t elt = APR_ARRAY_IDX(sorted_cat, i,
-                                                   svn_sort__item_t);
-              svn_error_t *err;
-
-              svn_pool_clear(subpool);
-              err = svn_client__record_wc_mergeinfo(elt.key,
-                                                    elt.value,
-                                                    TRUE, ctx,
-                                                    subpool);
-
-              if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
-                {
-                  /* PATH isn't just missing, it's not even versioned as far
-                     as this working copy knows.  But it was included in
-                     MERGES, which means that the server knows about it.
-                     Likely we don't have access to the source due to authz
-                     restrictions.  For now just clear the error and
-                     continue... */
-                  svn_error_clear(err);
-                }
-              else
-                {
-                  SVN_ERR(err);
-                }
-            }
-        }
+      SVN_ERR(svn_client__record_wc_mergeinfo_catalog(add_result_catalog,
+                                                      ctx, scratch_pool));
     }
 
   svn_pool_destroy(subpool);
@@ -9402,7 +9323,7 @@ merge_locked(const char *source1,
         {
           ancestral = TRUE;
           SVN_ERR(normalize_merge_sources_internal(
-                    &merge_sources, URL1, source_repos_root.url, rev1,
+                    &merge_sources, URL1, rev1,
                     svn_rangelist__initialize(rev1, yc_rev, TRUE, scratch_pool),
                     ra_session1, ctx, scratch_pool, scratch_pool));
         }
@@ -9412,7 +9333,7 @@ merge_locked(const char *source1,
         {
           ancestral = TRUE;
           SVN_ERR(normalize_merge_sources_internal(
-                    &merge_sources, URL2, source_repos_root.url, rev2,
+                    &merge_sources, URL2, rev2,
                     svn_rangelist__initialize(yc_rev, rev2, TRUE, scratch_pool),
                     ra_session2, ctx, scratch_pool, scratch_pool));
         }
@@ -9426,8 +9347,7 @@ merge_locked(const char *source1,
                                                        URL1, rev1,
                                                        URL2, rev2,
                                                        yc_rev,
-                                                       &source_repos_root,
-                                                       &wc_repos_root,
+                                                       same_repos,
                                                        depth,
                                                        ignore_ancestry, force,
                                                        record_only, dry_run,
@@ -10195,8 +10115,9 @@ find_unmerged_mergeinfo(svn_mergeinfo_ca
    reintegrate source relative to the root of the repository.
 
    SUBTREES_WITH_MERGEINFO is a hash of (const char *) absolute paths mapped
-   to (svn_string_t *) mergeinfo values for each working copy path with
-   explicit mergeinfo in TARGET_ABSPATH.
+   to (svn_mergeinfo_t *) mergeinfo values for each working copy path with
+   explicit mergeinfo in TARGET_ABSPATH.  Actually we only need to know the
+   paths, not the mergeinfo.
 
    TARGET_REV is the working revision the entire WC tree rooted at
    TARGET_REPOS_REL_PATH is at.  SOURCE_REV is the peg revision of the
@@ -10258,7 +10179,7 @@ calculate_left_hand_side(const char **ur
   if (!apr_hash_get(subtrees_with_mergeinfo, target_abspath,
                     APR_HASH_KEY_STRING))
     apr_hash_set(subtrees_with_mergeinfo, target_abspath,
-                 APR_HASH_KEY_STRING, svn_string_create_empty(result_pool));
+                 APR_HASH_KEY_STRING, apr_hash_make(result_pool));
 
   /* Get the history (segments) for TARGET_ABSPATH and any of its subtrees
      with explicit mergeinfo. */
@@ -10267,31 +10188,11 @@ calculate_left_hand_side(const char **ur
        hi = apr_hash_next(hi))
     {
       const char *absolute_path = svn__apr_hash_index_key(hi);
-      svn_string_t *mergeinfo_string = svn__apr_hash_index_val(hi);
       const char *path_rel_to_session;
       const char *path_rel_to_root;
-      svn_mergeinfo_t subtree_mergeinfo;
-      svn_error_t *err;
 
       svn_pool_clear(iterpool);
 
-      /* Issue #3896: If invalid mergeinfo in the reintegrate target
-         prevents us from proceeding, then raise the best error possible. */
-      err = svn_mergeinfo_parse(&subtree_mergeinfo, mergeinfo_string->data,
-                                iterpool);
-      if (err)
-        {
-          if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
-            {
-              err = svn_error_createf(
-                SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
-                _("Invalid mergeinfo detected on '%s', "
-                  "reintegrate merge not possible"),
-                svn_dirent_local_style(absolute_path, scratch_pool));
-            }
-          return svn_error_trace(err);
-        }
-
       /* Convert the absolute path with mergeinfo on it to a path relative
          to the session root. */
       SVN_ERR(svn_client__path_relative_to_root(&path_rel_to_root,
@@ -10426,7 +10327,6 @@ find_reintegrate_merge(svn_ra_session_t 
                        apr_pool_t *scratch_pool)
 {
   url_uuid_t wc_repos_root, source_repos_root;
-  svn_opt_revision_t working_revision = { svn_opt_revision_working, { 0 } };
   svn_ra_session_t *target_ra_session;
   svn_ra_session_t *source_ra_session;
   const char *source_repos_rel_path, *target_repos_rel_path;
@@ -10436,6 +10336,7 @@ find_reintegrate_merge(svn_ra_session_t 
   svn_revnum_t rev1, rev2;
   svn_mergeinfo_t unmerged_to_source_mergeinfo_catalog;
   svn_mergeinfo_t merged_to_source_mergeinfo_catalog;
+  svn_error_t *err;
   apr_hash_t *subtrees_with_mergeinfo;
   const char *target_url;
   svn_revnum_t target_base_rev;
@@ -10504,10 +10405,14 @@ find_reintegrate_merge(svn_ra_session_t 
                                "can be the root of the repository"));
 
   /* Find all the subtrees in TARGET_WCPATH that have explicit mergeinfo. */
-  SVN_ERR(svn_client_propget4(&subtrees_with_mergeinfo, SVN_PROP_MERGEINFO,
-                              target_abspath, &working_revision,
-                              &working_revision, NULL, svn_depth_infinity,
-                              NULL, ctx, scratch_pool, scratch_pool));
+  err = get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
+                                          target_abspath, svn_depth_infinity,
+                                          ctx, scratch_pool, scratch_pool);
+  /* Issue #3896: If invalid mergeinfo in the reintegrate target
+     prevents us from proceeding, then raise the best error possible. */
+  if (err && err->apr_err == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
+    err = svn_error_quick_wrap(err, _("Reintegrate merge not possible"));
+  SVN_ERR(err);
 
   /* Open two RA sessions, one to our source and one to our target. */
   SVN_ERR(svn_client__ra_session_from_path(&source_ra_session, &rev2, &url2,
@@ -10673,14 +10578,16 @@ do_reintegrate_merge(svn_ra_session_t *s
      ### related" in this source file).  We can merge to trunk without
      ### implementing this. */
   err = merge_cousins_and_supplement_mergeinfo(target_abspath,
-                                               target_ra_session,  /* ### */
-                                               source_ra_session,  /* wrong */
+                                               target_ra_session,
+                                               source_ra_session,
                                                url1, rev1, url2, rev2,
                                                yc_ancestor_rev,
-                                               &source_repos_root, /*   way */
-                                               &wc_repos_root,     /* round? */
+                                               TRUE /* same_repos */,
                                                svn_depth_infinity,
-                                               FALSE, FALSE, FALSE, dry_run,
+                                               FALSE /* ignore_ancestry */,
+                                               FALSE /* force */,
+                                               FALSE /* record_only */,
+                                               dry_run,
                                                merge_options, &use_sleep,
                                                ctx, scratch_pool);
 
@@ -10842,8 +10749,7 @@ merge_peg_locked(const char *source,
   SVN_ERR(svn_ra_get_uuid2(ra_session, &source_repos_root.uuid, scratch_pool));
 
   /* Normalize our merge sources. */
-  SVN_ERR(normalize_merge_sources(&merge_sources, source, URL,
-                                  source_repos_root.url, peg_revision,
+  SVN_ERR(normalize_merge_sources(&merge_sources, source, URL, peg_revision,
                                   ranges_to_merge, ra_session, ctx,
                                   scratch_pool, scratch_pool));
 



Mime
View raw message