svn commit: r1922048 [3/3] - in /subversion/branches/apply-processor/subversion/libsvn_client: client.h merge.c merge_processor.c

[email protected]
Newsgroups gmane.comp.version-control.subversion.svn
Message-ID <[email protected]>
Copied: subversion/branches/apply-processor/subversion/libsvn_client/merge_processor.c (from r1922042, subversion/branches/apply-processor/subversion/libsvn_client/merge.c)
URL: http://svn.apache.org/viewvc/subversion/branches/apply-processor/subversion/libsvn_client/merge_processor.c?p2=subversion/branches/apply-processor/subversion/libsvn_client/merge_processor.c&p1=subversion/branches/apply-processor/subversion/libsvn_client/merge.c&r1=1922042&r2=1922048&rev=1922048&view=diff
==============================================================================
--- subversion/branches/apply-processor/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/apply-processor/subversion/libsvn_client/merge_processor.c Sun Nov 24 14:10:54 2024
@@ -1,5 +1,6 @@
 /*
- * merge.c: merging
+ * merge_processor.c: svn_diff_tree_processor implementation for merge
+ *                    apply operation.
  *
  * ====================================================================
  *    Licensed to the Apache Software Foundation (ASF) under one
@@ -65,173 +66,11 @@
 #include "svn_private_config.h"
 
 
-/*-----------------------------------------------------------------------*/
-
-/* MERGEINFO MERGE SOURCE NORMALIZATION
- *
- * Nearly any helper function herein that accepts two URL/revision
- * pairs (or equivalent struct merge_source_t) expects one of two things
- * to be true:
- *
- *    1.  that mergeinfo is not being recorded at all for this
- *        operation, or
- *
- *    2.  that the pairs represent two locations along a single line
- *        of version history such that there are no copies in the
- *        history of the object between the locations when treating
- *        the oldest of the two locations as non-inclusive.  In other
- *        words, if there is a copy at all between them, there is only
- *        one copy and its source was the oldest of the two locations.
- *
- * We use svn_ra_get_location_segments() to split a given range of
- * revisions across an object's history into several which obey these
- * rules.  For example, an extract from the log of Subversion's own
- * /subversion/tags/1.4.5 directory shows the following copies between
- * r859500 and r866500 (omitting the '/subversion' prefix for clarity):
- *
- *    r859598:
- *      A /branches/1.4.x  (from /trunk:859597)
- *
- *    r865417:
- *      A /tags/1.4.4      (from /branches/1.4.x:865262)
- *    # Notice that this copy leaves a gap between 865262 and 865417.
- *
- *    r866420:
- *      A /branches/1.4.5  (from /tags/1.4.4:866419)
- *
- *    r866425:
- *      D /branches/1.4.5
- *      A /tags/1.4.5      (from /branches/1.4.5:866424)
- *
- * In graphical form:
- *
- *                859500 859597 865262        866419 866424 866500
- *                  .      .      .             .      .      .
- *    trunk       ------------------------------------------------
- *                         \      .             .      .
- *    branches/1.4.x        A-------------------------------------
- *                          .     \______       .      .
- *                          .            \      .      .
- *    tags/1.4.4            .             A-----------------------
- *                          .             .     \      .
- *    branches/1.4.5        .             .      A------D
- *                          .             .      .     \.
- *    tags/1.4.5            .             .      .      A---------
- *                          .             .      .      .
- *                       859598        865417 866420 866425
- *
- * A merge of the difference between r859500 and r866500 of this directory
- * gets split into sequential merges of the following location pairs.
- *
- *                859500 859597 865262 865416 866419 866424 866500
- *                  .      .      .      .      .      .      .
- *    trunk         (======]      .      .      .      .      .
- *                                .      .      .      .      .
- *    trunk                (      .      .      .      .      .
- *    branches/1.4.x        ======]      .      .      .      .
- *                                       .      .      .      .
- *    branches/1.4.x              (      .      .      .      .
- *    tags/1.4.4                   =============]      .      .
- *    implicit_src_gap            (======]      .      .      .
- *                                              .      .      .
- *    tags/1.4.4                                (      .      .
- *    branches/1.4.5                             ======]      .
- *                                                     .      .
- *    branches/1.4.5                                   (      .
- *    tags/1.4.5                                        ======]
- *
- * which are represented in merge_source_t as:
- *
- *    [/trunk:859500, /trunk:859597]
- *    (recorded in svn:mergeinfo as /trunk:859501-859597)
- *
- *    [/trunk:859597, /branches/1.4.x:865262]
- *    (recorded in svn:mergeinfo as /branches/1.4.x:859598-865262)
- *
- *    [/branches/1.4.x:865262, /tags/1.4.4@866419]
- *    (recorded in svn:mergeinfo as /tags/1.4.4:865263-866419)
- *    (and there is a gap, the revision range [865262, 865416])
- *
- *    [/tags/1.4.4@866419, /branches/1.4.5@866424]
- *    (recorded in svn:mergeinfo as /branches/1.4.5:866420-866424)
- *
- *    [/branches/1.4.5@866424, /tags/1.4.5@866500]
- *    (recorded in svn:mergeinfo as /tags/1.4.5:866425-866500)
- *
- * Our helper functions would then operate on one of these location
- * pairs at a time.
- */
-
-/* WHICH SVN_CLIENT_MERGE* API DO I WANT?
- *
- * libsvn_client has three public merge APIs; they are all wrappers
- * around the do_merge engine.  Which one to use depends on the number
- * of URLs passed as arguments and whether or not specific merge
- * ranges (-c/-r) are specified.
- *
- *                 1 URL                        2 URLs
- * +----+--------------------------------+---------------------+
- * | -c |       mergeinfo-driven         |                     |
- * | or |        cherrypicking           |                     |
- * | -r |    (svn_client_merge_peg)      |                     |
- * |----+--------------------------------+                     |
- * |    |       mergeinfo-driven         |     unsupported     |
- * |    |  'cherry harvest', i.e. merge  |                     |
- * |    |  all revisions from URL that   |                     |
- * | no |  have not already been merged  |                     |
- * | -c |    (svn_client_merge_peg)      |                     |
- * | or +--------------------------------+---------------------+
- * | -r |      mergeinfo-driven          |   mergeinfo-writing |
- * |    |        whole-branch            |    diff-and-apply   |
- * |    |       heuristic merge          |  (svn_client_merge) |
- * |    | (svn_client_merge_reintegrate) |                     |
- * +----+--------------------------------+---------------------+
- *
- *
- */
-
-/* THE CHILDREN_WITH_MERGEINFO ARRAY
- *
- * Many of the helper functions in this file pass around an
- * apr_array_header_t *CHILDREN_WITH_MERGEINFO.  This is a depth first
- * sorted array filled with svn_client__merge_path_t * describing the
- * merge target and any of its subtrees which have explicit mergeinfo
- * or otherwise need special attention during a merge.
- *
- * During mergeinfo unaware merges, CHILDREN_WITH_MERGEINFO contains
- * contains only one element (added by do_mergeinfo_unaware_dir_merge)
- * describing a contiguous range to be merged to the WC merge target.
- *
- * During mergeinfo aware merges CHILDREN_WITH_MERGEINFO is created
- * by get_mergeinfo_paths() and outside of that function and its helpers
- * should always meet the criteria dictated in get_mergeinfo_paths()'s doc
- * string.  The elements of CHILDREN_WITH_MERGEINFO should never be NULL.
- *
- * For clarification on mergeinfo aware vs. mergeinfo unaware merges, see
- * the doc string for HONOR_MERGEINFO().
- */
-
-
-/*-----------------------------------------------------------------------*/
 
 /*** Repos-Diff Editor Callbacks ***/
 
-struct merge_cmd_baton_t;
-
-struct notify_begin_state_t
+typedef struct merge_apply_processor_baton_t
 {
-  /* Cache of which abspath was last notified. */
-  const char *last_abspath;
-
-  /* Reference to the main merge baton */
-  struct merge_cmd_baton_t *merge_b;
-
-  /* the wrapped notification callback */
-  svn_wc_notify_func2_t notify_func2;
-  void *notify_baton2;
-};
-
-typedef struct merge_cmd_baton_t {
   svn_boolean_t force_delete;         /* Delete a file/dir even if modified */
   svn_boolean_t dry_run;
   svn_boolean_t record_only;          /* Whether to merge only mergeinfo
@@ -240,77 +79,15 @@ typedef struct merge_cmd_baton_t {
                                          is the same repository as the
                                          target.  Defaults to FALSE if DRY_RUN
                                          is TRUE.*/
-  svn_boolean_t mergeinfo_capable;    /* Whether the merge source server
-                                         is capable of Merge Tracking. */
-  svn_boolean_t ignore_mergeinfo;     /* Don't honor mergeinfo; see
-                                         doc string of do_merge().  FALSE if
-                                         MERGE_SOURCE->ancestral is FALSE. */
-  svn_boolean_t diff_ignore_ancestry; /* Diff unrelated nodes as if related; see
-                                         doc string of do_merge().  FALSE if
-                                         MERGE_SOURCE->ancestral is FALSE. */
-  svn_boolean_t reintegrate_merge;    /* Whether this is a --reintegrate
-                                         merge or not. */
 
   /* Description of merge target node */
   const svn_client__merge_target_t *target;
 
-  /* The left and right URLs and revs.  The value of this field changes to
-     reflect the merge_source_t *currently* being merged by do_merge(). */
+  /* The left and right URLs and revs.  */
   svn_client__merge_source_t merge_source;
 
-  /* Rangelist containing single range which describes the gap, if any,
-     in the natural history of the merge source currently being processed.
-     See https://issues.apache.org/jira/browse/SVN-3432.
-     Updated during each call to do_directory_merge().  May be NULL if there
-     is no gap. */
-  svn_rangelist_t *implicit_src_gap;
-
-  /* Reference to the one-and-only CHILDREN_WITH_MERGEINFO (see global
-     comment) or a similar list for single-file-merges */
-  apr_array_header_t *children_with_mergeinfo;
-
   svn_client_ctx_t *ctx;              /* Client context for callbacks, etc. */
 
-  /* The list of any paths which remained in conflict after a
-     resolution attempt was made.  We track this in-memory, rather
-     than just using WC entry state, since the latter doesn't help us
-     when in dry_run mode.
-     ### And because we only want to resolve conflicts that were
-         generated by this merge, not pre-existing ones? */
-  apr_hash_t *conflicted_paths;
-
-  /* A list of absolute paths which had no explicit mergeinfo prior to the
-     merge but got explicit mergeinfo added by the merge.  This is populated
-     by merge_change_props() and is allocated in POOL so it is subject to the
-     lifetime limitations of POOL.  Is NULL if no paths are found which
-     meet the criteria or DRY_RUN is true. */
-  apr_hash_t *paths_with_new_mergeinfo;
-
-  /* A list of absolute paths whose mergeinfo doesn't need updating after
-     the merge. This can be caused by the removal of mergeinfo by the merge
-     or by deleting the node itself.  This is populated by merge_change_props()
-     and the delete callbacks and is allocated in POOL so it is subject to the
-     lifetime limitations of POOL.  Is NULL if no paths are found which
-     meet the criteria or DRY_RUN is true. */
-  apr_hash_t *paths_with_deleted_mergeinfo;
-
-  /* The list of absolute skipped paths, which should be examined and
-     cleared after each invocation of the callback.  The paths
-     are absolute.  Is NULL if MERGE_B->MERGE_SOURCE->ancestral and
-     MERGE_B->REINTEGRATE_MERGE are both false. */
-  apr_hash_t *skipped_abspaths;
-
-  /* The list of absolute merged paths.  Unused if MERGE_B->MERGE_SOURCE->ancestral
-     and MERGE_B->REINTEGRATE_MERGE are both false. */
-  apr_hash_t *merged_abspaths;
-
-  /* A hash of (const char *) absolute WC paths mapped to the same which
-     represent the roots of subtrees added by the merge. */
-  apr_hash_t *added_abspaths;
-
-  /* A list of tree conflict victim absolute paths which may be NULL. */
-  apr_hash_t *tree_conflicted_abspaths;
-
   /* The diff3_cmd in ctx->config, if any, else null.  We could just
      extract this as needed, but since more than one caller uses it,
      we just set it up when this baton is created. */
@@ -321,140 +98,12 @@ typedef struct merge_cmd_baton_t {
      generated conflict files. */
   const apr_array_header_t *ext_patterns;
 
-  /* RA sessions used throughout a merge operation.  Opened/re-parented
-     as needed.
-
-     NOTE: During the actual merge editor drive, RA_SESSION1 is used
-     for the primary editing and RA_SESSION2 for fetching additional
-     information -- as necessary -- from the repository.  So during
-     this phase of the merge, you *must not* reparent RA_SESSION1; use
-     (temporarily reparenting if you must) RA_SESSION2 instead.  */
-  svn_ra_session_t *ra_session1;
-  svn_ra_session_t *ra_session2;
-
-  /* During the merge, *USE_SLEEP is set to TRUE if a sleep will be required
-     afterwards to ensure timestamp integrity, or unchanged if not. */
-  svn_boolean_t *use_sleep;
-
-  /* Pool which has a lifetime limited to one iteration over a given
-     merge source, i.e. it is cleared on every call to do_directory_merge()
-     or do_file_merge() in do_merge(). */
-  apr_pool_t *pool;
-
   /* Our notification callback, that adds a 'begin' notification */
   svn_wc_notify_func2_t notify_func;
   void *notify_baton;
-  struct notify_begin_state_t notify_begin;
-
-} merge_cmd_baton_t;
-
-
-/* Return TRUE iff we should be taking account of mergeinfo in deciding what
-   changes to merge, for the merge described by MERGE_B.  Specifically, that
-   is if the merge source server is capable of merge tracking, the left-side
-   merge source is an ancestor of the right-side (or vice-versa), the merge
-   source is in the same repository as the merge target, and we are not
-   ignoring mergeinfo. */
-static svn_boolean_t
-HONOR_MERGEINFO(const merge_cmd_baton_t *merge_b)
-{
-  return (merge_b->mergeinfo_capable
-          && merge_b->merge_source.ancestral
-          && merge_b->same_repos
-          && (!merge_b->ignore_mergeinfo));
-}
 
+} merge_apply_processor_baton_t;
 
-/* Return TRUE iff we should be recording mergeinfo for the merge described
-   by MERGE_B.  Specifically, that is if we are honoring mergeinfo and the
-   merge is not a dry run.  */
-static svn_boolean_t
-RECORD_MERGEINFO(const merge_cmd_baton_t *merge_b)
-{
-  return (HONOR_MERGEINFO(merge_b)
-          && !merge_b->dry_run);
-}
-
-
-/*-----------------------------------------------------------------------*/
-
-/*** Utilities ***/
-
-/* Return TRUE iff the session URL of RA_SESSION is equal to URL.  Useful in
- * asserting preconditions. */
-static svn_boolean_t
-session_url_is(svn_ra_session_t *ra_session,
-               const char *url,
-               apr_pool_t *scratch_pool)
-{
-  const char *session_url;
-  svn_error_t *err
-    = svn_ra_get_session_url(ra_session, &session_url, scratch_pool);
-
-  SVN_ERR_ASSERT_NO_RETURN(! err);
-  return strcmp(url, session_url) == 0;
-}
-
-/* Return a new merge_source_t structure, allocated in RESULT_POOL,
- * initialized with deep copies of LOC1 and LOC2 and ANCESTRAL. */
-svn_client__merge_source_t *
-svn_client__merge_source_create(const svn_client__pathrev_t *loc1,
-                                const svn_client__pathrev_t *loc2,
-                                svn_boolean_t ancestral,
-                                apr_pool_t *result_pool)
-{
-  svn_client__merge_source_t *s
-    = apr_palloc(result_pool, sizeof(*s));
-
-  s->loc1 = svn_client__pathrev_dup(loc1, result_pool);
-  s->loc2 = svn_client__pathrev_dup(loc2, result_pool);
-  s->ancestral = ancestral;
-  return s;
-}
-
-/* Return a deep copy of SOURCE, allocated in RESULT_POOL. */
-svn_client__merge_source_t *
-svn_client__merge_source_dup(const svn_client__merge_source_t *source,
-                             apr_pool_t *result_pool)
-{
-  svn_client__merge_source_t *s = apr_palloc(result_pool, sizeof(*s));
-
-  s->loc1 = svn_client__pathrev_dup(source->loc1, result_pool);
-  s->loc2 = svn_client__pathrev_dup(source->loc2, result_pool);
-  s->ancestral = source->ancestral;
-  return s;
-}
-
-/* Decide whether ambiguous foreign merge should be a warning or an error */
-#define WITH_AMBIGUOUS_FOREIGN_MERGE_WARNING \
-          (SVN_VER_MAJOR == 1 && SVN_VER_MINOR < 16)
-
-#if WITH_AMBIGUOUS_FOREIGN_MERGE_WARNING
-/* Notify a warning, given in WARNING, if WARNING is non-null.
- *
- * We plan to replace this with a hard error in Subversion 1.16.
- * This clears the error object WARNING before returning.
- */
-static void
-notify_pre_1_16_warning(svn_error_t *warning,
-                        svn_client_ctx_t *ctx,
-                        apr_pool_t *pool)
-{
-  if (!warning)
-    return;
-
-  if (ctx->notify_func2)
-    {
-      svn_wc_notify_t *n
-        = svn_wc_create_notify("" /*path*/, svn_wc_notify_warning, pool);
-
-      n->err = svn_error_quick_wrap(warning,
-                 _("In Subversion 1.16 this warning will become a fatal error"));
-      ctx->notify_func2(ctx->notify_baton2, n, pool);
-    }
-  svn_error_clear(warning);
-}
-#endif
 
 /* Return SVN_ERR_UNSUPPORTED_FEATURE if URL is not inside the repository
    of LOCAL_ABSPATH.  Use SCRATCH_POOL for temporary allocations. */
@@ -474,75 +123,9 @@ check_repos_match(const svn_client__merg
   return SVN_NO_ERROR;
 }
 
-/* Decide whether LOCATION1 and LOCATION2 point to the same repository
- * (with the same root URL) or to two different repositories.
- *   - same repository root URL         -> set *SAME_REPOS true
- *   - different repositories           -> set *SAME_REPOS false
- *   - different URLs but same UUID     -> return an error
- *
- * The last case is unsupported for practical and historical reasons
- * (see issue #4874) even though different URLs pointing to the same or
- * equivalent repositories could be supported in principle.
- */
-static svn_error_t *
-is_same_repos(svn_boolean_t *same_repos,
-              const svn_client__pathrev_t *location1,
-              const char *path1,
-              const svn_client__pathrev_t *location2,
-              const char *path2,
-              const char *message)
-{
-  if (strcmp(location1->repos_root_url, location2->repos_root_url) == 0)
-    *same_repos = TRUE;
-  else if (strcmp(location1->repos_uuid, location2->repos_uuid) != 0)
-    *same_repos = FALSE;
-  else
-    {
-      svn_error_t *err
-        = svn_error_create(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, message);
-
-      return svn_error_quick_wrapf(err,
-               _("The locations '%s' and '%s' point to repositories with the "
-                 "same repository UUID using different repository root URLs "
-                 "('%s' and '%s')"),
-               path1, path2,
-               location1->repos_root_url, location2->repos_root_url);
-    }
-  return SVN_NO_ERROR;
-}
-
-/* Check that LOCATION1 and LOCATION2 point to the same repository, with
- * the same root URL.  If not, throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES
- * error mentioning PATH_OR_URL1 and PATH_OR_URL2.
- */
-static svn_error_t *
-check_same_repos(const svn_client__pathrev_t *location1,
-                 const char *path_or_url1,
-                 const svn_client__pathrev_t *location2,
-                 const char *path_or_url2,
-                 const char *message)
-{
-  svn_boolean_t same_repos;
-
-  SVN_ERR(is_same_repos(&same_repos,
-                        location1, path_or_url1, location2, path_or_url2,
-                        message));
-  if (! same_repos)
-    {
-      svn_error_t *err
-        = svn_error_create(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, message);
-
-      return svn_error_quick_wrapf(err,
-               _("The locations '%s' and '%s' point to different repositories "
-                 "(root URLs '%s' and '%s', and differing UUIDs)"),
-               path_or_url1, path_or_url2,
-               location1->repos_root_url, location2->repos_root_url);
-    }
-  return SVN_NO_ERROR;
-}
-
 /* Store LOCAL_ABSPATH in PATH_HASH after duplicating it into the pool
    containing PATH_HASH. */
+/* TODO: deduplicate with merge.c  */
 static APR_INLINE void
 store_path(apr_hash_t *path_hash, const char *local_abspath)
 {
@@ -552,28 +135,6 @@ store_path(apr_hash_t *path_hash, const
   svn_hash_sets(path_hash, dup_path, dup_path);
 }
 
-/* Store LOCAL_ABSPATH in *PATH_HASH_P after duplicating it into the pool
-   containing *PATH_HASH_P.  If *PATH_HASH_P is NULL, then first set
-   *PATH_HASH_P to a new hash allocated from POOL.  */
-static APR_INLINE void
-alloc_and_store_path(apr_hash_t **path_hash_p,
-                     const char *local_abspath,
-                     apr_pool_t *pool)
-{
-  if (! *path_hash_p)
-    *path_hash_p = apr_hash_make(pool);
-  store_path(*path_hash_p, local_abspath);
-}
-
-/* Return whether any WC path was put in conflict by the merge
-   operation corresponding to MERGE_B. */
-static APR_INLINE svn_boolean_t
-is_path_conflicted_by_merge(merge_cmd_baton_t *merge_b)
-{
-  return (merge_b->conflicted_paths &&
-          apr_hash_count(merge_b->conflicted_paths) > 0);
-}
-
 /* Return a state indicating whether the WC metadata matches the
  * node kind on disk of the local path LOCAL_ABSPATH.
  * Use MERGE_B to determine the dry-run details; particularly, if a dry run
@@ -589,13 +150,14 @@ is_path_conflicted_by_merge(merge_cmd_ba
  *
  * Optionally return a bit more info for interested users.
  **/
+/* TODO: deduplicate with merge.c  */
 static svn_error_t *
 perform_obstruction_check(svn_wc_notify_state_t *obstruction_state,
                           svn_boolean_t *deleted,
                           svn_boolean_t *excluded,
                           svn_node_kind_t *kind,
                           svn_depth_t *parent_depth,
-                          const merge_cmd_baton_t *merge_b,
+                          const merge_apply_processor_baton_t *merge_b,
                           const char *local_abspath,
                           apr_pool_t *scratch_pool)
 {
@@ -1086,7 +648,7 @@ static svn_error_t *
 prepare_merge_props_changed(const apr_array_header_t **prop_updates,
                             const char *local_abspath,
                             const apr_array_header_t *propchanges,
-                            merge_cmd_baton_t *merge_b,
+                            merge_apply_processor_baton_t *merge_b,
                             apr_pool_t *result_pool,
                             apr_pool_t *scratch_pool)
 {
@@ -1137,12 +699,14 @@ prepare_merge_props_changed(const apr_ar
       if (merge_b->merge_source.loc1->rev < merge_b->merge_source.loc2->rev
           || !merge_b->merge_source.ancestral)
         {
+#if TODO_FILTER_MERGEINFO
           if (HONOR_MERGEINFO(merge_b) || merge_b->reintegrate_merge)
             SVN_ERR(filter_self_referential_mergeinfo(&props,
                                                       local_abspath,
                                                       merge_b->ra_session2,
                                                       merge_b->ctx,
                                                       result_pool));
+#endif
         }
     }
   *prop_updates = props;
@@ -1176,13 +740,17 @@ prepare_merge_props_changed(const apr_ar
 
               if (!has_pristine_mergeinfo && prop->value)
                 {
+#if TODO_STORE_PATH
                   alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
                                        local_abspath, merge_b->pool);
+#endif
                 }
               else if (has_pristine_mergeinfo && !prop->value)
                 {
+#if TODO_STORE_PATH
                   alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
                                        local_abspath, merge_b->pool);
+#endif
                 }
             }
         }
@@ -1315,7 +883,7 @@ struct merge_file_baton_t
 /* Record the skip for future processing and (later) produce the
    skip notification */
 static svn_error_t *
-record_skip(merge_cmd_baton_t *merge_b,
+record_skip(merge_apply_processor_baton_t *merge_b,
             const char *local_abspath,
             svn_node_kind_t kind,
             svn_wc_notify_action_t action,
@@ -1326,11 +894,13 @@ record_skip(merge_cmd_baton_t *merge_b,
   if (merge_b->record_only)
     return SVN_NO_ERROR; /* ### Why? - Legacy compatibility */
 
+#if TODO_STORE_PATH
   if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
       && !(pdb && pdb->shadowed))
     {
       store_path(merge_b->skipped_abspaths, local_abspath);
     }
+#endif
 
   if (merge_b->notify_func)
     {
@@ -1367,7 +937,7 @@ find_nearest_ancestor_with_intersecting_
  * of the same names in svn_wc_tree_conflict_description_t.
  */
 static svn_error_t *
-record_tree_conflict(merge_cmd_baton_t *merge_b,
+record_tree_conflict(merge_apply_processor_baton_t *merge_b,
                      const char *local_abspath,
                      struct merge_dir_baton_t *parent_baton,
                      svn_node_kind_t local_node_kind,
@@ -1384,6 +954,7 @@ record_tree_conflict(merge_cmd_baton_t *
   if (merge_b->record_only)
     return SVN_NO_ERROR;
 
+#if TODO_STORE_PATH
   if (merge_b->merge_source.ancestral
       || merge_b->reintegrate_merge)
     {
@@ -1392,6 +963,7 @@ record_tree_conflict(merge_cmd_baton_t *
 
   alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
                        merge_b->pool);
+#endif
 
   if (!merge_b->dry_run)
     {
@@ -1426,6 +998,7 @@ record_tree_conflict(merge_cmd_baton_t *
             reason = svn_wc_conflict_reason_moved_here;
         }
 
+#if TODO_FILTER_MERGEINFO
       if (HONOR_MERGEINFO(merge_b) && merge_b->merge_source.ancestral)
         {
           struct svn_client__merge_source_t *source;
@@ -1460,6 +1033,7 @@ record_tree_conflict(merge_cmd_baton_t *
                                          result_pool, scratch_pool));
         }
       else
+#endif
         SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
                                        merge_left_node_kind,
                                        merge_right_node_kind,
@@ -1519,16 +1093,18 @@ record_tree_conflict(merge_cmd_baton_t *
    update_add notification
  */
 static svn_error_t *
-record_update_add(merge_cmd_baton_t *merge_b,
+record_update_add(merge_apply_processor_baton_t *merge_b,
                   const char *local_abspath,
                   svn_node_kind_t kind,
                   svn_boolean_t notify_replaced,
                   apr_pool_t *scratch_pool)
 {
+#if TODO_STORE_PATH
   if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
     {
       store_path(merge_b->merged_abspaths, local_abspath);
     }
+#endif
 
   if (merge_b->notify_func)
     {
@@ -1551,17 +1127,19 @@ record_update_add(merge_cmd_baton_t *mer
 /* Record the update for future processing and produce the
    update_update notification */
 static svn_error_t *
-record_update_update(merge_cmd_baton_t *merge_b,
+record_update_update(merge_apply_processor_baton_t *merge_b,
                      const char *local_abspath,
                      svn_node_kind_t kind,
                      svn_wc_notify_state_t content_state,
                      svn_wc_notify_state_t prop_state,
                      apr_pool_t *scratch_pool)
 {
+#if TODO_STORE_PATH
   if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
     {
       store_path(merge_b->merged_abspaths, local_abspath);
     }
+#endif
 
   if (merge_b->notify_func)
     {
@@ -1583,12 +1161,13 @@ record_update_update(merge_cmd_baton_t *
 /* Record the delete for future processing and for (later) producing the
    update_delete notification */
 static svn_error_t *
-record_update_delete(merge_cmd_baton_t *merge_b,
+record_update_delete(merge_apply_processor_baton_t *merge_b,
                      struct merge_dir_baton_t *parent_db,
                      const char *local_abspath,
                      svn_node_kind_t kind,
                      apr_pool_t *scratch_pool)
 {
+#if TODO_STORE_PATH
   /* Update the lists of merged, skipped, tree-conflicted and added paths. */
   if (merge_b->merge_source.ancestral
       || merge_b->reintegrate_merge)
@@ -1629,6 +1208,7 @@ record_update_delete(merge_cmd_baton_t *
             }
         }
     }
+#endif
 
   return SVN_NO_ERROR;
 }
@@ -1636,7 +1216,7 @@ record_update_delete(merge_cmd_baton_t *
 /* Notify the pending 'D'eletes, that were waiting to see if a matching 'A'dd
    might make them a 'R'eplace. */
 static svn_error_t *
-handle_pending_notifications(merge_cmd_baton_t *merge_b,
+handle_pending_notifications(merge_apply_processor_baton_t *merge_b,
                              struct merge_dir_baton_t *db,
                              apr_pool_t *scratch_pool)
 {
@@ -1672,7 +1252,7 @@ handle_pending_notifications(merge_cmd_b
    ancestors of operational merges
  */
 static svn_error_t *
-mark_dir_edited(merge_cmd_baton_t *merge_b,
+mark_dir_edited(merge_apply_processor_baton_t *merge_b,
                 struct merge_dir_baton_t *db,
                 const char *local_abspath,
                 apr_pool_t *scratch_pool)
@@ -1726,11 +1306,13 @@ mark_dir_edited(merge_cmd_baton_t *merge
                                scratch_pool);
         }
 
+#if TODO_STORE_PATH
       if (merge_b->merge_source.ancestral
           || merge_b->reintegrate_merge)
         {
           store_path(merge_b->skipped_abspaths, local_abspath);
         }
+#endif
     }
   else if (db->tree_conflict_reason != CONFLICT_REASON_NONE)
     {
@@ -1755,7 +1337,7 @@ mark_dir_edited(merge_cmd_baton_t *merge
    ancestors of operational merges
  */
 static svn_error_t *
-mark_file_edited(merge_cmd_baton_t *merge_b,
+mark_file_edited(merge_apply_processor_baton_t *merge_b,
                  struct merge_file_baton_t *fb,
                  const char *local_abspath,
                  apr_pool_t *scratch_pool)
@@ -1805,11 +1387,13 @@ mark_file_edited(merge_cmd_baton_t *merg
                                scratch_pool);
         }
 
+#if TODO_STORE_PATH
       if (merge_b->merge_source.ancestral
           || merge_b->reintegrate_merge)
         {
           store_path(merge_b->skipped_abspaths, local_abspath);
         }
+#endif
     }
   else if (fb->tree_conflict_reason != CONFLICT_REASON_NONE)
     {
@@ -1848,7 +1432,7 @@ merge_file_opened(void **new_file_baton,
                   apr_pool_t *result_pool,
                   apr_pool_t *scratch_pool)
 {
-  merge_cmd_baton_t *merge_b = processor->baton;
+  merge_apply_processor_baton_t *merge_b = processor->baton;
   struct merge_dir_baton_t *pdb = dir_baton;
   struct merge_file_baton_t *fb;
   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
@@ -2097,7 +1681,7 @@ merge_file_changed(const char *relpath,
                   const struct svn_diff_tree_processor_t *processor,
                   apr_pool_t *scratch_pool)
 {
-  merge_cmd_baton_t *merge_b = processor->baton;
+  merge_apply_processor_baton_t *merge_b = processor->baton;
   struct merge_file_baton_t *fb = file_baton;
   svn_client_ctx_t *ctx = merge_b->ctx;
   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
@@ -2154,11 +1738,13 @@ merge_file_changed(const char *relpath,
                                   NULL, NULL,
                                   ctx->cancel_func, ctx->cancel_baton,
                                   scratch_pool));
+#if TODO_STORE_PATH
       if (property_state == svn_wc_notify_state_conflicted)
         {
           alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
                                merge_b->pool);
         }
+#endif
     }
 
   /* Easy out: We are only applying mergeinfo differences. */
@@ -2218,12 +1804,14 @@ merge_file_changed(const char *relpath,
                             ctx->cancel_baton,
                             scratch_pool));
 
+#if TODO_STORE_PATH
       if (content_outcome == svn_wc_merge_conflict
           || property_state == svn_wc_notify_state_conflicted)
         {
           alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
                                merge_b->pool);
         }
+#endif
 
       if (content_outcome == svn_wc_merge_conflict)
         text_state = svn_wc_notify_state_conflicted;
@@ -2273,7 +1861,7 @@ merge_file_added(const char *relpath,
                  const struct svn_diff_tree_processor_t *processor,
                  apr_pool_t *scratch_pool)
 {
-  merge_cmd_baton_t *merge_b = processor->baton;
+  merge_apply_processor_baton_t *merge_b = processor->baton;
   struct merge_file_baton_t *fb = file_baton;
   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
                                               relpath, scratch_pool);
@@ -2304,12 +1892,14 @@ merge_file_added(const char *relpath,
       return SVN_NO_ERROR;
     }
 
+#if TODO_STORE_PATH
   if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
       && ( !fb->parent_baton || !fb->parent_baton->added))
     {
       /* Store the roots of added subtrees */
       store_path(merge_b->added_abspaths, local_abspath);
     }
+#endif
 
   if (!merge_b->dry_run)
     {
@@ -2337,11 +1927,13 @@ merge_file_added(const char *relpath,
           pristine_props = right_props; /* Includes last_* information */
           new_props = NULL; /* No local changes */
 
+#if TODO_STORE_PATH
           if (svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
             {
               alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
                                    local_abspath, merge_b->pool);
             }
+#endif
         }
       else
         {
@@ -2379,8 +1971,10 @@ merge_file_added(const char *relpath,
                                       merge_b->ctx->cancel_baton,
                                       scratch_pool));
 
+#if TODO_USE_SLEEP
       /* Caller must call svn_sleep_for_timestamps() */
       *merge_b->use_sleep = TRUE;
+#endif
     }
 
   SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_file,
@@ -2514,7 +2108,7 @@ merge_file_deleted(const char *relpath,
                    const struct svn_diff_tree_processor_t *processor,
                    apr_pool_t *scratch_pool)
 {
-  merge_cmd_baton_t *merge_b = processor->baton;
+  merge_apply_processor_baton_t *merge_b = processor->baton;
   struct merge_file_baton_t *fb = file_baton;
   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
                                               relpath, scratch_pool);
@@ -2555,9 +2149,11 @@ merge_file_deleted(const char *relpath,
     {
       if (same)
         {
+#if TODO_STORE_PATH
           /* Note that we checked this file */
           store_path(fb->parent_baton->delete_state->compared_abspaths,
                      local_abspath);
+#endif
         }
       else
         {
@@ -2577,9 +2173,11 @@ merge_file_deleted(const char *relpath,
                                NULL, NULL /* no notify */,
                                scratch_pool));
 
+#if TODO_STORE_PATH
       /* Record that we might have deleted mergeinfo */
       alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
                            local_abspath, merge_b->pool);
+#endif
 
       /* And notify the deletion */
       SVN_ERR(record_update_delete(merge_b, fb->parent_baton, local_abspath,
@@ -2631,7 +2229,7 @@ merge_dir_opened(void **new_dir_baton,
                  apr_pool_t *result_pool,
                  apr_pool_t *scratch_pool)
 {
-  merge_cmd_baton_t *merge_b = processor->baton;
+  merge_apply_processor_baton_t *merge_b = processor->baton;
   struct merge_dir_baton_t *db;
   struct merge_dir_baton_t *pdb = parent_dir_baton;
 
@@ -3029,7 +2627,7 @@ merge_dir_changed(const char *relpath,
                   const struct svn_diff_tree_processor_t *processor,
                   apr_pool_t *scratch_pool)
 {
-  merge_cmd_baton_t *merge_b = processor->baton;
+  merge_apply_processor_baton_t *merge_b = processor->baton;
   struct merge_dir_baton_t *db = dir_baton;
   const apr_array_header_t *props;
   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
@@ -3077,11 +2675,13 @@ merge_dir_changed(const char *relpath,
                                   ctx->cancel_func, ctx->cancel_baton,
                                   scratch_pool));
 
+#if TODO_STORE_PATH
       if (prop_state == svn_wc_notify_state_conflicted)
         {
           alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
                                merge_b->pool);
         }
+#endif
 
       if (prop_state == svn_wc_notify_state_conflicted
           || prop_state == svn_wc_notify_state_merged
@@ -3116,7 +2716,7 @@ merge_dir_added(const char *relpath,
                 const struct svn_diff_tree_processor_t *processor,
                 apr_pool_t *scratch_pool)
 {
-  merge_cmd_baton_t *merge_b = processor->baton;
+  merge_apply_processor_baton_t *merge_b = processor->baton;
   struct merge_dir_baton_t *db = dir_baton;
   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
                                               relpath, scratch_pool);
@@ -3144,12 +2744,14 @@ merge_dir_added(const char *relpath,
                  && ! merge_b->record_only /* Skip details from merge_open_dir() */
                  );
 
+#if TODO_STORE_PATH
   if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
       && ( !db->parent_baton || !db->parent_baton->added))
     {
       /* Store the roots of added subtrees */
       store_path(merge_b->added_abspaths, local_abspath);
     }
+#endif
 
   if (merge_b->same_repos)
     {
@@ -3194,11 +2796,13 @@ merge_dir_added(const char *relpath,
                                                 scratch_pool));
         }
 
+#if TODO_STORE_PATH
       if (svn_hash_gets(new_pristine_props, SVN_PROP_MERGEINFO))
         {
           alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
                                local_abspath, merge_b->pool);
         }
+#endif
     }
   else
     {
@@ -3230,11 +2834,13 @@ merge_dir_added(const char *relpath,
                                   merge_b->ctx->cancel_func,
                                   merge_b->ctx->cancel_baton,
                                   scratch_pool));
+#if TODO_STORE_PATH
       if (prop_state == svn_wc_notify_state_conflicted)
         {
           alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
                                merge_b->pool);
         }
+#endif
     }
 
   return SVN_NO_ERROR;
@@ -3283,7 +2889,7 @@ merge_dir_deleted(const char *relpath,
                   const struct svn_diff_tree_processor_t *processor,
                   apr_pool_t *scratch_pool)
 {
-  merge_cmd_baton_t *merge_b = processor->baton;
+  merge_apply_processor_baton_t *merge_b = processor->baton;
   struct merge_dir_baton_t *db = dir_baton;
   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
                                               relpath, scratch_pool);
@@ -3428,8 +3034,10 @@ merge_dir_deleted(const char *relpath,
       if (working_props
           && svn_hash_gets(working_props, SVN_PROP_MERGEINFO))
         {
+#if TODO_STORE_PATH
           alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
                                local_abspath, merge_b->pool);
+#endif
         }
 
       SVN_ERR(record_update_delete(merge_b, db->parent_baton, local_abspath,
@@ -3455,7 +3063,7 @@ merge_dir_closed(const char *relpath,
                  const struct svn_diff_tree_processor_t *processor,
                  apr_pool_t *scratch_pool)
 {
-  merge_cmd_baton_t *merge_b = processor->baton;
+  merge_apply_processor_baton_t *merge_b = processor->baton;
   struct merge_dir_baton_t *db = dir_baton;
 
   SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
@@ -3482,7 +3090,7 @@ merge_node_absent(const char *relpath,
                   const svn_diff_tree_processor_t *processor,
                   apr_pool_t *scratch_pool)
 {
-  merge_cmd_baton_t *merge_b = processor->baton;
+  merge_apply_processor_baton_t *merge_b = processor->baton;
   struct merge_dir_baton_t *db = dir_baton;
 
   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
@@ -3495,15 +3103,41 @@ merge_node_absent(const char *relpath,
   return SVN_NO_ERROR;
 }
 
-/* Return a diff processor that will apply the merge to the WC.
- */
-static svn_diff_tree_processor_t *
-merge_apply_processor(merge_cmd_baton_t *merge_cmd_baton,
-                      apr_pool_t *result_pool)
+svn_diff_tree_processor_t *
+svn_client__apply_processor_create(const svn_client__merge_target_t *target,
+                                   const svn_client__merge_source_t *source,
+                                   svn_wc_notify_func2_t notify_func,
+                                   void *notify_baton,
+                                   svn_boolean_t same_repos,
+                                   svn_boolean_t force_delete,
+                                   svn_boolean_t record_only,
+                                   svn_boolean_t dry_run,
+                                   const char *diff3_cmd,
+                                   const apr_array_header_t *merge_options,
+                                   const apr_array_header_t *ext_patterns,
+                                   svn_client_ctx_t *ctx,
+                                   apr_pool_t *scratch_pool,
+                                   apr_pool_t *result_pool)
 {
   svn_diff_tree_processor_t *merge_processor;
 
-  merge_processor = svn_diff__tree_processor_create(merge_cmd_baton,
+  merge_apply_processor_baton_t *baton =
+    apr_pcalloc(result_pool, sizeof(*baton));
+
+  baton->target = target;
+  baton->merge_source = *source;
+  baton->diff3_cmd = diff3_cmd;
+  baton->merge_options = merge_options;
+  baton->ext_patterns = ext_patterns;
+  baton->same_repos = same_repos;
+  baton->force_delete = force_delete;
+  baton->record_only = record_only;
+  baton->dry_run = dry_run;
+  baton->notify_func = notify_func;
+  baton->notify_baton = notify_baton;
+  baton->ctx = ctx;
+
+  merge_processor = svn_diff__tree_processor_create(baton,
                                                     result_pool);
 
   merge_processor->dir_opened   = merge_dir_opened;
@@ -3525,6 +3159,7 @@ merge_apply_processor(merge_cmd_baton_t
 
 /* Initialize minimal dir baton to allow calculating 'R'eplace
    from 'D'elete + 'A'dd. */
+/* TODO: doesn't yet works */
 static void *
 open_dir_for_replace_single_file(apr_pool_t *result_pool)
 {
@@ -3537,9508 +3172,3 @@ open_dir_for_replace_single_file(apr_poo
 
   return dir_baton;
 }
-
-/*-----------------------------------------------------------------------*/
-
-/*** Merge Notification ***/
-
-
-/* Finds a nearest ancestor in CHILDREN_WITH_MERGEINFO for LOCAL_ABSPATH. If
-   PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
-   where child->abspath == PATH is considered PATH's ancestor.  If FALSE,
-   then child->abspath must be a proper ancestor of PATH.
-
-   CHILDREN_WITH_MERGEINFO is expected to be sorted in Depth first
-   order of path. */
-static svn_client__merge_path_t *
-find_nearest_ancestor(const apr_array_header_t *children_with_mergeinfo,
-                      svn_boolean_t path_is_own_ancestor,
-                      const char *local_abspath)
-{
-  int i;
-
-  SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
-
-  for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
-    {
-      svn_client__merge_path_t *child =
-        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
-
-      if (svn_dirent_is_ancestor(child->abspath, local_abspath)
-          && (path_is_own_ancestor
-              || strcmp(child->abspath, local_abspath) != 0))
-        return child;
-    }
-  return NULL;
-}
-
-/* Find the highest level path in a merge target (possibly the merge target
-   itself) to use in a merge notification header.
-
-   Return the svn_client__merge_path_t * representing the most distant
-   ancestor in CHILDREN_WITH_MERGEINFO of LOCAL_ABSPATH where said
-   ancestor's first remaining ranges element (per the REMAINING_RANGES
-   member of the ancestor) intersect with the first remaining ranges element
-   for every intermediate ancestor svn_client__merge_path_t * of
-   LOCAL_ABSPATH.  If no such ancestor is found return NULL.
-
-   If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
-   represent a forward merge, then set *START to the oldest revision found
-   in any of the intersecting ancestors and *END to the youngest revision
-   found.  If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
-   represent a reverse merge, then set *START to the youngest revision
-   found and *END to the oldest revision found.  If no ancestors are found
-   then set *START and *END to SVN_INVALID_REVNUM.
-
-   If PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
-   where child->abspath == PATH is considered PATH's ancestor.  If FALSE,
-   then child->abspath must be a proper ancestor of PATH.
-
-   See the CHILDREN_WITH_MERGEINFO ARRAY global comment for more
-   information. */
-static svn_client__merge_path_t *
-find_nearest_ancestor_with_intersecting_ranges(
-  svn_revnum_t *start,
-  svn_revnum_t *end,
-  const apr_array_header_t *children_with_mergeinfo,
-  svn_boolean_t path_is_own_ancestor,
-  const char *local_abspath)
-{
-  int i;
-  svn_client__merge_path_t *nearest_ancestor = NULL;
-
-  *start = SVN_INVALID_REVNUM;
-  *end = SVN_INVALID_REVNUM;
-
-  SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
-
-  for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
-    {
-      svn_client__merge_path_t *child =
-        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
-
-      if (svn_dirent_is_ancestor(child->abspath, local_abspath)
-          && (path_is_own_ancestor
-              || strcmp(child->abspath, local_abspath) != 0))
-        {
-          if (nearest_ancestor == NULL)
-            {
-              /* Found an ancestor. */
-              nearest_ancestor = child;
-
-              if (child->remaining_ranges)
-                {
-                  svn_merge_range_t *r1 = APR_ARRAY_IDX(
-                    child->remaining_ranges, 0, svn_merge_range_t *);
-                  *start = r1->start;
-                  *end = r1->end;
-                }
-              else
-                {
-                  /* If CHILD->REMAINING_RANGES is null then LOCAL_ABSPATH
-                     is inside an absent subtree in the merge target. */
-                  *start = SVN_INVALID_REVNUM;
-                  *end = SVN_INVALID_REVNUM;
-                  break;
-                }
-            }
-          else
-            {
-              /* We'e found another ancestor for LOCAL_ABSPATH.  Do its
-                 first remaining range intersect with the previously
-                 found ancestor? */
-              svn_merge_range_t *r1 =
-                APR_ARRAY_IDX(nearest_ancestor->remaining_ranges, 0,
-                              svn_merge_range_t *);
-              svn_merge_range_t *r2 =
-                APR_ARRAY_IDX(child->remaining_ranges, 0,
-                              svn_merge_range_t *);
-
-              if (r1 && r2)
-                {
-                  svn_merge_range_t range1;
-                  svn_merge_range_t range2;
-                  svn_boolean_t reverse_merge = r1->start > r2->end;
-
-                  /* Flip endpoints if this is a reverse merge. */
-                  if (reverse_merge)
-                    {
-                      range1.start = r1->end;
-                      range1.end = r1->start;
-                      range2.start = r2->end;
-                      range2.end = r2->start;
-                    }
-                  else
-                    {
-                      range1.start = r1->start;
-                      range1.end = r1->end;
-                      range2.start = r2->start;
-                      range2.end = r2->end;
-                    }
-
-                  if (range1.start < range2.end && range2.start < range1.end)
-                    {
-                      *start = reverse_merge ?
-                        MAX(r1->start, r2->start) : MIN(r1->start, r2->start);
-                      *end = reverse_merge ?
-                        MIN(r1->end, r2->end) : MAX(r1->end, r2->end);
-                      nearest_ancestor = child;
-                    }
-                }
-            }
-        }
-    }
-  return nearest_ancestor;
-}
-
-/* Notify that we're starting to record mergeinfo for the merge of the
- * revision range RANGE into TARGET_ABSPATH.  RANGE should be null if the
- * merge sources are not from the same URL.
- *
- * This calls the client's notification receiver (as found in the client
- * context), with a WC abspath.
- */
-static void
-notify_mergeinfo_recording(const char *target_abspath,
-                           const svn_merge_range_t *range,
-                           svn_client_ctx_t *ctx,
-                           apr_pool_t *pool)
-{
-  if (ctx->notify_func2)
-    {
-      svn_wc_notify_t *n = svn_wc_create_notify(
-        target_abspath, svn_wc_notify_merge_record_info_begin, pool);
-
-      n->merge_range = range ? svn_merge_range_dup(range, pool) : NULL;
-      ctx->notify_func2(ctx->notify_baton2, n, pool);
-    }
-}
-
-/* Notify that we're completing the merge into TARGET_ABSPATH.
- *
- * This calls the client's notification receiver (as found in the client
- * context), with a WC abspath.
- */
-static void
-notify_merge_completed(const char *target_abspath,
-                       svn_client_ctx_t *ctx,
-                       apr_pool_t *pool)
-{
-  if (ctx->notify_func2)
-    {
-      svn_wc_notify_t *n
-        = svn_wc_create_notify(target_abspath, svn_wc_notify_merge_completed,
-                               pool);
-      ctx->notify_func2(ctx->notify_baton2, n, pool);
-    }
-}
-
-
-/* Remove merge source gaps from range used for merge notifications.
-   See https://issues.apache.org/jira/browse/SVN-4138
-
-   If IMPLICIT_SRC_GAP is not NULL then it is a rangelist containing a
-   single range (see the implicit_src_gap member of merge_cmd_baton_t).
-   RANGE describes a (possibly reverse) merge.
-
-   If IMPLICIT_SRC_GAP is not NULL and it's sole range intersects with
-   the older revision in *RANGE, then remove IMPLICIT_SRC_GAP's range
-   from *RANGE. */
-static void
-remove_source_gap(svn_merge_range_t *range,
-                  apr_array_header_t *implicit_src_gap)
-{
-  if (implicit_src_gap)
-    {
-      svn_merge_range_t *gap_range =
-        APR_ARRAY_IDX(implicit_src_gap, 0, svn_merge_range_t *);
-      if (range->start < range->end)
-        {
-          if (gap_range->start == range->start)
-            range->start = gap_range->end;
-        }
-      else /* Reverse merge */
-        {
-          if (gap_range->start == range->end)
-            range->end = gap_range->end;
-        }
-    }
-}
-
-/* Notify that we're starting a merge
- *
- * This calls the client's notification receiver (as found in the client
- * context), with a WC abspath.
- */
-static void
-notify_merge_begin(struct notify_begin_state_t *notify_begin_state,
-                   const char *local_abspath,
-                   svn_boolean_t delete_action,
-                   apr_pool_t *scratch_pool)
-{
-  merge_cmd_baton_t *merge_b = notify_begin_state->merge_b;
-  svn_wc_notify_t *notify;
-  svn_merge_range_t n_range =
-    {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE};
-  const char *notify_abspath;
-
-  if (! notify_begin_state->notify_func2)
-    return;
-
-  /* If our merge sources are ancestors of one another... */
-  if (merge_b->merge_source.ancestral)
-    {
-      const svn_client__merge_path_t *child;
-      /* Find LOCAL_ABSPATH's nearest ancestor in
-         CHILDREN_WITH_MERGEINFO.  Normally we consider a child in
-         CHILDREN_WITH_MERGEINFO representing PATH to be an
-         ancestor of PATH, but if this is a deletion of PATH then the
-         notification must be for a proper ancestor of PATH.  This ensures
-         we don't get notifications like:
-
-           --- Merging rX into 'PARENT/CHILD'
-           D    PARENT/CHILD
-
-         But rather:
-
-           --- Merging rX into 'PARENT'
-           D    PARENT/CHILD
-      */
-
-      child = find_nearest_ancestor_with_intersecting_ranges(
-        &(n_range.start), &(n_range.end),
-        merge_b->children_with_mergeinfo,
-        ! delete_action, local_abspath);
-
-      if (!child && delete_action)
-        {
-          /* Triggered by file replace in single-file-merge */
-          child = find_nearest_ancestor(merge_b->children_with_mergeinfo,
-                                        TRUE, local_abspath);
-        }
-
-      assert(child != NULL); /* Should always find the merge anchor */
-
-      if (! child)
-        return;
-
-      if (notify_begin_state->last_abspath != NULL
-          && strcmp(child->abspath, notify_begin_state->last_abspath) == 0)
-        {
-          /* Don't notify the same merge again */
-          return;
-        }
-
-      notify_begin_state->last_abspath = child->abspath;
-
-      if (child->absent || child->remaining_ranges->nelts == 0
-          || !SVN_IS_VALID_REVNUM(n_range.start))
-        {
-          /* No valid information for an header */
-          return;
-        }
-
-      notify_abspath = child->abspath;
-    }
-  else
-    {
-      if (notify_begin_state->last_abspath)
-        return; /* already notified */
-
-      notify_abspath = merge_b->target->abspath;
-      /* Store something in last_abspath. Any value would do */
-      notify_begin_state->last_abspath = merge_b->target->abspath;
-    }
-
-  notify = svn_wc_create_notify(notify_abspath,
-                                merge_b->same_repos
-                                      ? svn_wc_notify_merge_begin
-                                      : svn_wc_notify_foreign_merge_begin,
-                                scratch_pool);
-
-  if (SVN_IS_VALID_REVNUM(n_range.start))
-    {
-      /* If the merge source has a gap, then don't mention
-         those gap revisions in the notification. */
-      remove_source_gap(&n_range, merge_b->implicit_src_gap);
-      notify->merge_range = &n_range;
-    }
-  else
-    {
-      notify->merge_range = NULL;
-    }
-
-  notify_begin_state->notify_func2(notify_begin_state->notify_baton2, notify,
-                                   scratch_pool);
-}
-
-/* Our notification callback, that adds a 'begin' notification */
-static void
-notify_merging(void *baton,
-               const svn_wc_notify_t *notify,
-               apr_pool_t *pool)
-{
-  struct notify_begin_state_t *b = baton;
-
-  notify_merge_begin(b, notify->path,
-                     notify->action == svn_wc_notify_update_delete,
-                     pool);
-
-  b->notify_func2(b->notify_baton2, notify, pool);
-}
-
-/* Set *OUT_RANGELIST to the intersection of IN_RANGELIST with the simple
- * (inheritable) revision range REV1:REV2, according to CONSIDER_INHERITANCE.
- * If REV1 is equal to REV2, the result is an empty rangelist, otherwise
- * REV1 must be less than REV2.
- *
- * Note: If CONSIDER_INHERITANCE is FALSE, the effect is to treat any non-
- * inheritable input ranges as if they were inheritable.  If it is TRUE, the
- * effect is to discard any non-inheritable input ranges.  Therefore the
- * ranges in *OUT_RANGELIST will always be inheritable. */
-static svn_error_t *
-rangelist_intersect_range(svn_rangelist_t **out_rangelist,
-                          const svn_rangelist_t *in_rangelist,
-                          svn_revnum_t rev1,
-                          svn_revnum_t rev2,
-                          svn_boolean_t consider_inheritance,
-                          apr_pool_t *result_pool,
-                          apr_pool_t *scratch_pool)
-{
-  SVN_ERR_ASSERT(rev1 <= rev2);
-
-  if (rev1 < rev2)
-    {
-      svn_rangelist_t *simple_rangelist =
-        svn_rangelist__initialize(rev1, rev2, TRUE, scratch_pool);
-
-      SVN_ERR(svn_rangelist_intersect(out_rangelist,
-                                      simple_rangelist, in_rangelist,
-                                      consider_inheritance, result_pool));
-    }
-  else
-    {
-      *out_rangelist = apr_array_make(result_pool, 0,
-                                      sizeof(svn_merge_range_t *));
-    }
-  return SVN_NO_ERROR;
-}
-
-/* Helper for fix_deleted_subtree_ranges().  Like fix_deleted_subtree_ranges()
-   this function should only be called when honoring mergeinfo.
-
-   CHILD, PARENT, REVISION1, REVISION2, and RA_SESSION are all cascaded from
-   fix_deleted_subtree_ranges() -- see that function for more information on
-   each.
-
-   If PARENT is not the merge target then PARENT must have already have been
-   processed by this function as a child.  Specifically, this means that
-   PARENT->REMAINING_RANGES must already be populated -- it can be an empty
-   rangelist but cannot be NULL.
-
-   PRIMARY_URL is the merge source url of CHILD at the younger of REVISION1
-   and REVISION2.
-
-   Since this function is only invoked for subtrees of the merge target, the
-   guarantees afforded by normalize_merge_sources() don't apply - see the
-   'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file.
-   Therefore it is possible that PRIMARY_URL@REVISION1 and
-   PRIMARY_URL@REVISION2 don't describe the endpoints of an unbroken line of
-   history.  The purpose of this helper is to identify these cases of broken
-   history and adjust CHILD->REMAINING_RANGES in such a way we don't later try
-   to describe nonexistent path/revisions to the merge report editor -- see
-   drive_merge_report_editor().
-
-   If PRIMARY_URL@REVISION1 and PRIMARY_URL@REVISION2 describe an unbroken
-   line of history then do nothing and leave CHILD->REMAINING_RANGES as-is.
-
-   If neither PRIMARY_URL@REVISION1 nor PRIMARY_URL@REVISION2 exist then
-   there is nothing to merge to CHILD->ABSPATH so set CHILD->REMAINING_RANGES
-   equal to PARENT->REMAINING_RANGES.  This will cause the subtree to
-   effectively ignore CHILD -- see 'Note: If the first svn_merge_range_t...'
-   in drive_merge_report_editor()'s doc string.
-
-   If PRIMARY_URL@REVISION1 *xor* PRIMARY_URL@REVISION2 exist then we take the
-   subset of REVISION1:REVISION2 in CHILD->REMAINING_RANGES at which
-   PRIMARY_URL doesn't exist and set that subset equal to
-   PARENT->REMAINING_RANGES' intersection with that non-existent range.  Why?
-   Because this causes CHILD->REMAINING_RANGES to be identical to
-   PARENT->REMAINING_RANGES for revisions between REVISION1 and REVISION2 at
-   which PRIMARY_URL doesn't exist.  As mentioned above this means that
-   drive_merge_report_editor() won't attempt to describe these non-existent
-   subtree path/ranges to the reporter (which would break the merge).
-
-   If the preceding paragraph wasn't terribly clear then what follows spells
-   out this function's behavior a bit more explicitly:
-
-   For forward merges (REVISION1 < REVISION2)
-
-     If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then
-     find the revision 'N' in which PRIMARY_URL@REVISION1 was deleted.  Leave
-     the subset of CHILD->REMAINING_RANGES that intersects with
-     REVISION1:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES
-     that intersects with (N - 1):REVISION2 equal to PARENT->REMAINING_RANGES'
-     intersection with (N - 1):REVISION2.
-
-     If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does,
-     then find the revision 'M' in which PRIMARY_URL@REVISION2 came into
-     existence.  Leave the subset of CHILD->REMAINING_RANGES that intersects with
-     (M - 1):REVISION2 as-is and set the subset of CHILD->REMAINING_RANGES
-     that intersects with REVISION1:(M - 1) equal to PARENT->REMAINING_RANGES'
-     intersection with REVISION1:(M - 1).
-
-   For reverse merges (REVISION1 > REVISION2)
-
-     If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then
-     find the revision 'N' in which PRIMARY_URL@REVISION1 came into existence.
-     Leave the subset of CHILD->REMAINING_RANGES that intersects with
-     REVISION2:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES
-     that intersects with (N - 1):REVISION1 equal to PARENT->REMAINING_RANGES'
-     intersection with (N - 1):REVISION1.
-
-     If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does,
-     then find the revision 'M' in which PRIMARY_URL@REVISION2 came into
-     existence.  Leave the subset of CHILD->REMAINING_RANGES that intersects with
-     REVISION2:(M - 1) as-is and set the subset of CHILD->REMAINING_RANGES
-     that intersects with (M - 1):REVISION1 equal to PARENT->REMAINING_RANGES'
-     intersection with REVISION1:(M - 1).
-
-   SCRATCH_POOL is used for all temporary allocations.  Changes to CHILD are
-   allocated in RESULT_POOL. */
-static svn_error_t *
-adjust_deleted_subtree_ranges(svn_client__merge_path_t *child,
-                              svn_client__merge_path_t *parent,
-                              svn_revnum_t revision1,
-                              svn_revnum_t revision2,
-                              const char *primary_url,
-                              svn_ra_session_t *ra_session,
-                              svn_client_ctx_t *ctx,
-                              apr_pool_t *result_pool,
-                              apr_pool_t *scratch_pool)
-{
-  svn_boolean_t is_rollback = revision2 < revision1;
-  svn_revnum_t younger_rev = is_rollback ? revision1 : revision2;
-  svn_revnum_t peg_rev = younger_rev;
-  svn_revnum_t older_rev = is_rollback ? revision2 : revision1;
-  apr_array_header_t *segments;
-  svn_error_t *err;
-
-  SVN_ERR_ASSERT(parent->remaining_ranges);
-
-  err = svn_client__repos_location_segments(&segments, ra_session,
-                                            primary_url, peg_rev,
-                                            younger_rev, older_rev, ctx,
-                                            scratch_pool);
-
-  if (err)
-    {
-      const char *rel_source_path;  /* PRIMARY_URL relative to RA_SESSION */
-      svn_node_kind_t kind;
-
-      if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
-        return svn_error_trace(err);
-
-      svn_error_clear(err);
-
-      /* PRIMARY_URL@peg_rev doesn't exist.  Check if PRIMARY_URL@older_rev
-         exists, if neither exist then the editor can simply ignore this
-         subtree. */
-
-      SVN_ERR(svn_ra_get_path_relative_to_session(
-                ra_session, &rel_source_path, primary_url, scratch_pool));
-
-      SVN_ERR(svn_ra_check_path(ra_session, rel_source_path,
-                                older_rev, &kind, scratch_pool));
-      if (kind == svn_node_none)
-        {
-          /* Neither PRIMARY_URL@peg_rev nor PRIMARY_URL@older_rev exist,
-             so there is nothing to merge.  Set CHILD->REMAINING_RANGES
-             identical to PARENT's. */
-          child->remaining_ranges =
-            svn_rangelist_dup(parent->remaining_ranges, scratch_pool);
-        }
-      else
-        {
-          svn_rangelist_t *deleted_rangelist;
-          svn_revnum_t rev_primary_url_deleted;
-
-          /* PRIMARY_URL@older_rev exists, so it was deleted at some
-             revision prior to peg_rev, find that revision. */
-          SVN_ERR(svn_ra_get_deleted_rev(ra_session, rel_source_path,
-                                         older_rev, younger_rev,
-                                         &rev_primary_url_deleted,
-                                         scratch_pool));
-
-          /* PRIMARY_URL@older_rev exists and PRIMARY_URL@peg_rev doesn't,
-             so svn_ra_get_deleted_rev() should always find the revision
-             PRIMARY_URL@older_rev was deleted. */
-          SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev_primary_url_deleted));
-
-          /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
-             PARENT->REMAINING_RANGES so both will work with the
-             svn_rangelist_* APIs below. */
-          if (is_rollback)
-            {
-              /* svn_rangelist_reverse operates in place so it's safe
-                 to use our scratch_pool. */
-              SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
-                                            scratch_pool));
-              SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
-                                            scratch_pool));
-            }
-
-          /* Find the intersection of CHILD->REMAINING_RANGES with the
-             range over which PRIMARY_URL@older_rev exists (ending at
-             the youngest revision at which it still exists). */
-          SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
-                                            child->remaining_ranges,
-                                            older_rev,
-                                            rev_primary_url_deleted - 1,
-                                            FALSE,
-                                            scratch_pool, scratch_pool));
-
-          /* Merge into CHILD->REMAINING_RANGES the intersection of
-             PARENT->REMAINING_RANGES with the range beginning when
-             PRIMARY_URL@older_rev was deleted until younger_rev. */
-          SVN_ERR(rangelist_intersect_range(&deleted_rangelist,
-                                            parent->remaining_ranges,
-                                            rev_primary_url_deleted - 1,
-                                            peg_rev,
-                                            FALSE,
-                                            scratch_pool, scratch_pool));
-          SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
-                                       deleted_rangelist, scratch_pool,
-                                       scratch_pool));
-
-          /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
-             to reverse order if necessary. */
-          if (is_rollback)
-            {
-              SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
-                                            scratch_pool));
-              SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
-                                            scratch_pool));
-            }
-        }
-    }
-  else /* PRIMARY_URL@peg_rev exists. */
-    {
-      svn_rangelist_t *non_existent_rangelist;
-      svn_location_segment_t *segment =
-        APR_ARRAY_IDX(segments, (segments->nelts - 1),
-                      svn_location_segment_t *);
-
-      /* We know PRIMARY_URL@peg_rev exists as the call to
-         svn_client__repos_location_segments() succeeded.  If there is only
-         one segment that starts at oldest_rev then we know that
-         PRIMARY_URL@oldest_rev:PRIMARY_URL@peg_rev describes an unbroken
-         line of history, so there is nothing more to adjust in
-         CHILD->REMAINING_RANGES. */
-      if (segment->range_start == older_rev)
-        {
-          return SVN_NO_ERROR;
-        }
-
-      /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
-         PARENT->REMAINING_RANGES so both will work with the
-         svn_rangelist_* APIs below. */
-      if (is_rollback)
-        {
-          SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
-                                        scratch_pool));
-          SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
-                                        scratch_pool));
-        }
-
-      /* Intersect CHILD->REMAINING_RANGES with the range where PRIMARY_URL
-         exists.  Since segment doesn't span older_rev:peg_rev we know
-         PRIMARY_URL@peg_rev didn't come into existence until
-         segment->range_start + 1. */
-      SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
-                                        child->remaining_ranges,
-                                        segment->range_start, peg_rev,
-                                        FALSE, scratch_pool, scratch_pool));
-
-      /* Merge into CHILD->REMAINING_RANGES the intersection of
-         PARENT->REMAINING_RANGES with the range before PRIMARY_URL@peg_rev
-         came into existence. */
-      SVN_ERR(rangelist_intersect_range(&non_existent_rangelist,
-                                        parent->remaining_ranges,
-                                        older_rev, segment->range_start,
-                                        FALSE, scratch_pool, scratch_pool));
-      SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
-                                   non_existent_rangelist, scratch_pool,
-                                   scratch_pool));
-
-      /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
-         to reverse order if necessary. */
-      if (is_rollback)
-        {
-          SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
-                                        scratch_pool));
-          SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
-                                        scratch_pool));
-        }
-    }
-
-  /* Make a lasting copy of CHILD->REMAINING_RANGES using POOL. */
-  child->remaining_ranges = svn_rangelist_dup(child->remaining_ranges,
-                                              result_pool);
-  return SVN_NO_ERROR;
-}
-
-/* Helper for do_directory_merge().
-
-   SOURCE is cascaded from the argument of the same name in
-   do_directory_merge().  TARGET is the merge target.  RA_SESSION is the
-   session for the younger of SOURCE->loc1 and SOURCE->loc2.
-
-   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
-   CHILDREN_WITH_MERGEINFO are allocated in RESULT_POOL.
-*/
-static svn_error_t *
-fix_deleted_subtree_ranges(const svn_client__merge_source_t *source,
-                           const svn_client__merge_target_t *target,
-                           svn_ra_session_t *ra_session,
-                           apr_array_header_t *children_with_mergeinfo,
-                           svn_client_ctx_t *ctx,
-                           apr_pool_t *result_pool,
-                           apr_pool_t *scratch_pool)
-{
-  int i;
-  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-  svn_boolean_t is_rollback = source->loc2->rev < source->loc1->rev;
-
-  assert(session_url_is(ra_session,
-                        (is_rollback ? source->loc1 : source->loc2)->url,
-                        scratch_pool));
-
-  /* CHILDREN_WITH_MERGEINFO is sorted in depth-first order, so
-     start at index 1 to examine only subtrees. */
-  for (i = 1; 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_client__merge_path_t *parent;
-      svn_rangelist_t *deleted_rangelist, *added_rangelist;
-
-      SVN_ERR_ASSERT(child);
-      if (child->absent)
-        continue;
-
-      svn_pool_clear(iterpool);
-
-      /* Find CHILD's parent. */
-      parent = find_nearest_ancestor(children_with_mergeinfo,
-                                     FALSE, child->abspath);
-
-      /* Since CHILD is a subtree then its parent must be in
-         CHILDREN_WITH_MERGEINFO, see the global comment
-         'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
-      SVN_ERR_ASSERT(parent);
-
-      /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
-         so it will work with the svn_rangelist_diff API. */
-      if (is_rollback)
-        {
-          SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
-          SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
-        }
-
-      SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
-                                 child->remaining_ranges,
-                                 parent->remaining_ranges,
-                                 TRUE, iterpool));
-
-      if (is_rollback)
-        {
-          SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
-          SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
-        }
-
-      /* If CHILD is the merge target we then know that SOURCE is provided
-         by normalize_merge_sources() -- see 'MERGEINFO MERGE SOURCE
-         NORMALIZATION'.  Due to this normalization we know that SOURCE
-         describes an unbroken line of history such that the entire range
-         described by SOURCE can potentially be merged to CHILD.
-
-         But if CHILD is a subtree we don't have the same guarantees about
-         SOURCE as we do for the merge target.  SOURCE->loc1 and/or
-         SOURCE->loc2 might not exist.
-
-         If one or both doesn't exist, then adjust CHILD->REMAINING_RANGES
-         such that we don't later try to describe invalid subtrees in
-         drive_merge_report_editor(), as that will break the merge.
-         If CHILD has the same remaining ranges as PARENT however, then
-         there is no need to make these adjustments, since
-         drive_merge_report_editor() won't attempt to describe CHILD in this
-         case, see the 'Note' in drive_merge_report_editor's docstring. */
-      if (deleted_rangelist->nelts || added_rangelist->nelts)
-        {
-          const char *child_primary_source_url;
-          const char *child_repos_src_path =
-            svn_dirent_is_child(target->abspath, child->abspath, iterpool);
-
-          /* This loop is only processing subtrees, so CHILD->ABSPATH
-             better be a proper child of the merge target. */
-          SVN_ERR_ASSERT(child_repos_src_path);
-
-          child_primary_source_url =
-            svn_path_url_add_component2((source->loc1->rev < source->loc2->rev)
-                                        ? source->loc2->url : source->loc1->url,
-                                        child_repos_src_path, iterpool);
-
-          SVN_ERR(adjust_deleted_subtree_ranges(child, parent,
-                                                source->loc1->rev,
-                                                source->loc2->rev,
-                                                child_primary_source_url,
-                                                ra_session,
-                                                ctx, result_pool, iterpool));
-        }
-    }
-
-  svn_pool_destroy(iterpool);
-  return SVN_NO_ERROR;
-}
-
-/*-----------------------------------------------------------------------*/
-
-/*** Determining What Remains To Be Merged ***/
-
-/* Get explicit and/or implicit mergeinfo for the working copy path
-   TARGET_ABSPATH.
-
-   If RECORDED_MERGEINFO is not NULL then set *RECORDED_MERGEINFO
-   to TARGET_ABSPATH's explicit or inherited mergeinfo as dictated by
-   INHERIT.
-
-   If IMPLICIT_MERGEINFO is not NULL then set *IMPLICIT_MERGEINFO
-   to TARGET_ABSPATH's implicit mergeinfo (a.k.a. natural history).
-
-   If both RECORDED_MERGEINFO and IMPLICIT_MERGEINFO are not NULL and
-   *RECORDED_MERGEINFO is inherited, then *IMPLICIT_MERGEINFO will be
-   removed from *RECORDED_MERGEINFO.
-
-   If INHERITED is not NULL set *INHERITED to TRUE if *RECORDED_MERGEINFO
-   is inherited rather than explicit.  If RECORDED_MERGEINFO is NULL then
-   INHERITED is ignored.
-
-
-   If IMPLICIT_MERGEINFO is not NULL then START and END are limits on
-   the natural history sought, must both be valid revision numbers, and
-   START must be greater than END.  If TARGET_ABSPATH's base revision
-   is older than START, then the base revision is used as the younger
-   bound in place of START.
-
-   RA_SESSION is an RA session open to the repository in which TARGET_ABSPATH
-   lives.  It may be temporarily reparented as needed by this function.
-
-   Allocate *RECORDED_MERGEINFO and *IMPLICIT_MERGEINFO in RESULT_POOL.
-   Use SCRATCH_POOL for any temporary allocations. */
-static svn_error_t *
-get_full_mergeinfo(svn_mergeinfo_t *recorded_mergeinfo,
-                   svn_mergeinfo_t *implicit_mergeinfo,
-                   svn_boolean_t *inherited,
-                   svn_mergeinfo_inheritance_t inherit,
-                   svn_ra_session_t *ra_session,
-                   const char *target_abspath,
-                   svn_revnum_t start,
-                   svn_revnum_t end,
-                   svn_client_ctx_t *ctx,
-                   apr_pool_t *result_pool,
-                   apr_pool_t *scratch_pool)
-{
-  /* First, we get the real mergeinfo. */
-  if (recorded_mergeinfo)
-    {
-      SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(recorded_mergeinfo,
-                                                    inherited,
-                                                    NULL /* from_repos */,
-                                                    FALSE,
-                                                    inherit, ra_session,
-                                                    target_abspath,
-                                                    ctx, result_pool));
-    }
-
-  if (implicit_mergeinfo)
-    {
-      svn_client__pathrev_t *target;
-
-      /* Assert that we have sane input. */
-      SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start) && SVN_IS_VALID_REVNUM(end)
-                     && (start > end));
-
-      /* Retrieve the origin (original_*) of the node, or just the
-         url if the node was not copied. */
-      SVN_ERR(svn_client__wc_node_get_origin(&target, target_abspath, ctx,
-                                             scratch_pool, scratch_pool));
-
-      if (! target)
-        {
-          /* We've been asked to operate on a locally added target, so its
-           * implicit mergeinfo is empty. */
-          *implicit_mergeinfo = apr_hash_make(result_pool);
-        }
-      else if (target->rev <= end)
-        {
-          /* We're asking about a range outside our natural history
-             altogether.  That means our implicit mergeinfo is empty. */
-          *implicit_mergeinfo = apr_hash_make(result_pool);
-        }
-      else
-        {
-          /* Fetch so-called "implicit mergeinfo" (that is, natural
-             history). */
-
-          /* Do not ask for implicit mergeinfo from TARGET_ABSPATH's future.
-             TARGET_ABSPATH might not even exist, and even if it does the
-             working copy is *at* TARGET_REV so its implicit history ends
-             at TARGET_REV! */
-          if (target->rev < start)
-            start = target->rev;
-
-          /* Fetch the implicit mergeinfo. */
-          SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo,
-                                                       NULL,
-                                                       target, start, end,
-                                                       ra_session, ctx,
-                                                       result_pool));
-        }
-    } /*if (implicit_mergeinfo) */
-
-  return SVN_NO_ERROR;
-}
-
-/* Helper for ensure_implicit_mergeinfo().
-
-   PARENT, CHILD, REVISION1, REVISION2 and CTX
-   are all cascaded from the arguments of the same names in
-   ensure_implicit_mergeinfo().  PARENT and CHILD must both exist, i.e.
-   this function should never be called where CHILD is the merge target.
-
-   If PARENT->IMPLICIT_MERGEINFO is NULL, obtain it from the server.
-
-   Set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
-   PARENT->IMPLICIT_MERGEINFO.  CHILD->IMPLICIT_MERGEINFO is allocated
-   in RESULT_POOL.
-
-   RA_SESSION is an RA session open to the repository that contains CHILD.
-   It may be temporarily reparented by this function.
-   */
-static svn_error_t *
-inherit_implicit_mergeinfo_from_parent(svn_client__merge_path_t *parent,
-                                       svn_client__merge_path_t *child,
-                                       svn_revnum_t revision1,
-                                       svn_revnum_t revision2,
-                                       svn_ra_session_t *ra_session,
-                                       svn_client_ctx_t *ctx,
-                                       apr_pool_t *result_pool,
-                                       apr_pool_t *scratch_pool)
-{
-  const char *path_diff;
-
-  /* This only works on subtrees! */
-  SVN_ERR_ASSERT(parent);
-  SVN_ERR_ASSERT(child);
-
-  /* While PARENT must exist, it is possible we've deferred
-     getting its implicit mergeinfo.  If so get it now. */
-  if (!parent->implicit_mergeinfo)
-    SVN_ERR(get_full_mergeinfo(NULL, &(parent->implicit_mergeinfo),
-                               NULL, svn_mergeinfo_inherited,
-                               ra_session, child->abspath,
-                               MAX(revision1, revision2),
-                               MIN(revision1, revision2),
-                               ctx, result_pool, scratch_pool));
-
-  /* Let CHILD inherit PARENT's implicit mergeinfo. */
-
-  path_diff = svn_dirent_is_child(parent->abspath, child->abspath,
-                                  scratch_pool);
-  /* PARENT->PATH better be an ancestor of CHILD->ABSPATH! */
-  SVN_ERR_ASSERT(path_diff);
-
-  SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
-            &child->implicit_mergeinfo, parent->implicit_mergeinfo,
-            path_diff, result_pool, scratch_pool));
-  child->implicit_mergeinfo = svn_mergeinfo_dup(child->implicit_mergeinfo,
-                                                result_pool);
-  return SVN_NO_ERROR;
-}
-
-/* Helper of filter_merged_revisions().
-
-   If we have deferred obtaining CHILD->IMPLICIT_MERGEINFO, then get
-   it now, allocating it in RESULT_POOL.  If CHILD_INHERITS_PARENT is true
-   then set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
-   PARENT->IMPLICIT_MERGEINFO, otherwise contact the repository.  Use
-   SCRATCH_POOL for all temporary allocations.
-
-   RA_SESSION is an RA session open to the repository that contains CHILD.
-   It may be temporarily reparented by this function.
-
-   PARENT, CHILD, REVISION1, REVISION2 and
-   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 *
-ensure_implicit_mergeinfo(svn_client__merge_path_t *parent,
-                          svn_client__merge_path_t *child,
-                          svn_boolean_t child_inherits_parent,
-                          svn_revnum_t revision1,
-                          svn_revnum_t revision2,
-                          svn_ra_session_t *ra_session,
-                          svn_client_ctx_t *ctx,
-                          apr_pool_t *result_pool,
-                          apr_pool_t *scratch_pool)
-{
-  /* If we haven't already found CHILD->IMPLICIT_MERGEINFO then
-     contact the server to get it. */
-
-  if (child->implicit_mergeinfo)
-    return SVN_NO_ERROR;
-
-  if (child_inherits_parent)
-    SVN_ERR(inherit_implicit_mergeinfo_from_parent(parent,
-                                                   child,
-                                                   revision1,
-                                                   revision2,
-                                                   ra_session,
-                                                   ctx,
-                                                   result_pool,
-                                                   scratch_pool));
-  else
-    SVN_ERR(get_full_mergeinfo(NULL,
-                               &(child->implicit_mergeinfo),
-                               NULL, svn_mergeinfo_inherited,
-                               ra_session, child->abspath,
-                               MAX(revision1, revision2),
-                               MIN(revision1, revision2),
-                               ctx, result_pool, scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
-/* Helper for calculate_remaining_ranges().
-
-   Initialize CHILD->REMAINING_RANGES to a rangelist representing the
-   requested merge of REVISION1:REVISION2 from MERGEINFO_PATH to
-   CHILD->ABSPATH.
-
-   For forward merges remove any ranges from CHILD->REMAINING_RANGES that
-   have already been merged to CHILD->ABSPATH per TARGET_MERGEINFO or
-   CHILD->IMPLICIT_MERGEINFO.  For reverse merges remove any ranges from
-   CHILD->REMAINING_RANGES that have not already been merged to CHILD->ABSPATH
-   per TARGET_MERGEINFO or CHILD->IMPLICIT_MERGEINFO.  If we have deferred
-   obtaining CHILD->IMPLICIT_MERGEINFO and it is necessary to use it for
-   these calculations, then get it from the server, allocating it in
-   RESULT_POOL.
-
-   CHILD represents a working copy path which is the merge target or one of
-   the target's subtrees.  If not NULL, PARENT is CHILD's nearest path-wise
-   ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'.
-
-   If the function needs to consider CHILD->IMPLICIT_MERGEINFO and
-   CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the
-   mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO.  Otherwise contact
-   the repository for CHILD->IMPLICIT_MERGEINFO.
-
-   NOTE: If PARENT is present then this function must have previously been
-   called for PARENT, i.e. if populate_remaining_ranges() is calling this
-   function for a set of svn_client__merge_path_t* the calls must be made
-   in depth-first order.
-
-   MERGEINFO_PATH is the merge source relative to the repository root.
-
-   REVISION1 and REVISION2 describe the merge range requested from
-   MERGEINFO_PATH.
-
-   TARGET_RANGELIST is the portion of CHILD->ABSPATH's explicit or inherited
-   mergeinfo that intersects with the merge history described by
-   MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2.  TARGET_RANGELIST
-   should be NULL if there is no explicit or inherited mergeinfo on
-   CHILD->ABSPATH or an empty list if CHILD->ABSPATH has empty mergeinfo or
-   explicit mergeinfo that exclusively describes non-intersecting history
-   with MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2.
-
-   SCRATCH_POOL is used for all temporary allocations.
-
-   NOTE: This should only be called when honoring mergeinfo.
-
-   NOTE: Like calculate_remaining_ranges() if PARENT is present then this
-   function must have previously been called for PARENT.
-*/
-static svn_error_t *
-filter_merged_revisions(svn_client__merge_path_t *parent,
-                        svn_client__merge_path_t *child,
-                        const char *mergeinfo_path,
-                        svn_rangelist_t *target_rangelist,
-                        svn_revnum_t revision1,
-                        svn_revnum_t revision2,
-                        svn_boolean_t child_inherits_implicit,
-                        svn_ra_session_t *ra_session,
-                        svn_client_ctx_t *ctx,
-                        apr_pool_t *result_pool,
-                        apr_pool_t *scratch_pool)
-{
-  svn_rangelist_t *requested_rangelist,
-    *target_implicit_rangelist, *explicit_rangelist;
-
-  /* Convert REVISION1 and REVISION2 to a rangelist.
-
-     Note: Talking about a requested merge range's inheritability
-     doesn't make much sense, but as we are using svn_merge_range_t
-     to describe it we need to pick *something*.  Since all the
-     rangelist manipulations in this function either don't consider
-     inheritance by default or we are requesting that they don't (i.e.
-     svn_rangelist_remove and svn_rangelist_intersect) then we could
-     set the inheritability as FALSE, it won't matter either way. */
-  requested_rangelist = svn_rangelist__initialize(revision1, revision2,
-                                                  TRUE, scratch_pool);
-
-  /* Now filter out revisions that have already been merged to CHILD. */
-
-  if (revision1 > revision2) /* This is a reverse merge. */
-    {
-      svn_rangelist_t *added_rangelist, *deleted_rangelist;
-
-      /* The revert range and will need to be reversed for
-         our svn_rangelist_* APIs to work properly. */
-      SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
-
-      /* Set EXPLICIT_RANGELIST to the list of source-range revs that are
-         already recorded as merged to target. */
-      if (target_rangelist)
-        {
-          /* Return the intersection of the revs which are both already
-             represented by CHILD's explicit or inherited mergeinfo.
-
-             We don't consider inheritance when determining intersecting
-             ranges.  If we *did* consider inheritance, then our calculation
-             would be wrong.  For example, if the CHILD->REMAINING_RANGES is
-             5:3 and TARGET_RANGELIST is r5* (non-inheritable) then the

[... 8421 lines stripped ...]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.