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 ...]


Reply via email to