Modified: subversion/branches/apply-processor/subversion/libsvn_client/merge.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/apply-processor/subversion/libsvn_client/merge.c?rev=1922048&r1=1922047&r2=1922048&view=diff
==============================================================================
--- subversion/branches/apply-processor/subversion/libsvn_client/merge.c 
(original)
+++ subversion/branches/apply-processor/subversion/libsvn_client/merge.c Sun 
Nov 24 14:10:54 2024
@@ -232,7 +232,6 @@ struct notify_begin_state_t
 };
 
 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
                                          differences. */
@@ -311,16 +310,6 @@ typedef struct merge_cmd_baton_t {
   /* 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. */
-  const char *diff3_cmd;
-  const apr_array_header_t *merge_options;
-
-  /* Array of file extension patterns to preserve as extensions in
-     generated conflict files. */
-  const apr_array_header_t *ext_patterns;
-
   /* RA sessions used throughout a merge operation.  Opened/re-parented
      as needed.
 
@@ -456,24 +445,6 @@ notify_pre_1_16_warning(svn_error_t *war
 }
 #endif
 
-/* Return SVN_ERR_UNSUPPORTED_FEATURE if URL is not inside the repository
-   of LOCAL_ABSPATH.  Use SCRATCH_POOL for temporary allocations. */
-static svn_error_t *
-check_repos_match(const svn_client__merge_target_t *target,
-                  const char *local_abspath,
-                  const char *url,
-                  apr_pool_t *scratch_pool)
-{
-  if (!svn_uri__is_ancestor(target->loc.repos_root_url, url))
-    return svn_error_createf(
-        SVN_ERR_UNSUPPORTED_FEATURE, NULL,
-         _("URL '%s' of '%s' is not in repository '%s'"),
-         url, svn_dirent_local_style(local_abspath, scratch_pool),
-         target->loc.repos_root_url);
-
-  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
@@ -628,2916 +599,6 @@ perform_obstruction_check(svn_wc_notify_
   return SVN_NO_ERROR;
 }
 
-/* Create *LEFT and *RIGHT conflict versions for conflict victim
- * at VICTIM_ABSPATH, with merge-left node kind MERGE_LEFT_NODE_KIND
- * and merge-right node kind MERGE_RIGHT_NODE_KIND, using information
- * obtained from MERGE_SOURCE and TARGET.
- * Allocate returned conflict versions in RESULT_POOL. */
-static svn_error_t *
-make_conflict_versions(const svn_wc_conflict_version_t **left,
-                       const svn_wc_conflict_version_t **right,
-                       const char *victim_abspath,
-                       svn_node_kind_t merge_left_node_kind,
-                       svn_node_kind_t merge_right_node_kind,
-                       const svn_client__merge_source_t *merge_source,
-                       const svn_client__merge_target_t *target,
-                       apr_pool_t *result_pool,
-                       apr_pool_t *scratch_pool)
-{
-  const char *child = svn_dirent_skip_ancestor(target->abspath,
-                                               victim_abspath);
-  const char *left_relpath, *right_relpath;
-
-  SVN_ERR_ASSERT(child != NULL);
-  left_relpath = svn_client__pathrev_relpath(merge_source->loc1,
-                                             scratch_pool);
-  right_relpath = svn_client__pathrev_relpath(merge_source->loc2,
-                                              scratch_pool);
-
-  *left = svn_wc_conflict_version_create2(
-            merge_source->loc1->repos_root_url,
-            merge_source->loc1->repos_uuid,
-            svn_relpath_join(left_relpath, child, scratch_pool),
-            merge_source->loc1->rev,
-            merge_left_node_kind, result_pool);
-
-  *right = svn_wc_conflict_version_create2(
-             merge_source->loc2->repos_root_url,
-             merge_source->loc2->repos_uuid,
-             svn_relpath_join(right_relpath, child, scratch_pool),
-             merge_source->loc2->rev,
-             merge_right_node_kind, result_pool);
-
-  return SVN_NO_ERROR;
-}
-
-/* Helper for filter_self_referential_mergeinfo()
-
-   *MERGEINFO is a non-empty, non-null collection of mergeinfo.
-
-   Remove all mergeinfo from *MERGEINFO that describes revision ranges
-   greater than REVISION.  Put a copy of any removed mergeinfo, allocated
-   in POOL, into *YOUNGER_MERGEINFO.
-
-   If no mergeinfo is removed from *MERGEINFO then *YOUNGER_MERGEINFO is set
-   to NULL.  If all mergeinfo is removed from *MERGEINFO then *MERGEINFO is
-   set to NULL.
-   */
-static svn_error_t*
-split_mergeinfo_on_revision(svn_mergeinfo_t *younger_mergeinfo,
-                            svn_mergeinfo_t *mergeinfo,
-                            svn_revnum_t revision,
-                            apr_pool_t *pool)
-{
-  apr_hash_index_t *hi;
-  apr_pool_t *iterpool = svn_pool_create(pool);
-
-  *younger_mergeinfo = NULL;
-  for (hi = apr_hash_first(pool, *mergeinfo); hi; hi = apr_hash_next(hi))
-    {
-      int i;
-      const char *merge_source_path = apr_hash_this_key(hi);
-      svn_rangelist_t *rangelist = apr_hash_this_val(hi);
-
-      svn_pool_clear(iterpool);
-
-      for (i = 0; i < rangelist->nelts; i++)
-        {
-          svn_merge_range_t *range =
-            APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
-          if (range->end <= revision)
-            {
-              /* This entirely of this range is as old or older than
-                 REVISION, so leave it in *MERGEINFO. */
-              continue;
-            }
-          else
-            {
-              /* Since the rangelists in svn_mergeinfo_t's are sorted in
-                 increasing order we know that part or all of *this* range
-                 and *all* of the remaining ranges in *RANGELIST are younger
-                 than REVISION.  Remove the younger rangelists from
-                 *MERGEINFO and put them in *YOUNGER_MERGEINFO. */
-              int j;
-              svn_rangelist_t *younger_rangelist =
-                apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
-
-              for (j = i; j < rangelist->nelts; j++)
-                {
-                  svn_merge_range_t *younger_range = svn_merge_range_dup(
-                    APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *), pool);
-
-                  /* REVISION might intersect with the first range where
-                     range->end > REVISION.  If that is the case then split
-                     the current range into two, putting the younger half
-                     into *YOUNGER_MERGEINFO and leaving the older half in
-                     *MERGEINFO. */
-                  if (j == i && range->start + 1 <= revision)
-                    younger_range->start = range->end = revision;
-
-                  APR_ARRAY_PUSH(younger_rangelist, svn_merge_range_t *) =
-                    younger_range;
-                }
-
-              /* So far we've only been manipulating rangelists, now we
-                 actually create *YOUNGER_MERGEINFO and then remove the older
-                 ranges from *MERGEINFO */
-              if (!(*younger_mergeinfo))
-                *younger_mergeinfo = apr_hash_make(pool);
-              svn_hash_sets(*younger_mergeinfo, merge_source_path,
-                            younger_rangelist);
-              SVN_ERR(svn_mergeinfo_remove2(mergeinfo, *younger_mergeinfo,
-                                            *mergeinfo, TRUE, pool, iterpool));
-              break; /* ...out of for (i = 0; i < rangelist->nelts; i++) */
-            }
-        }
-    }
-
-  svn_pool_destroy(iterpool);
-
-  return SVN_NO_ERROR;
-}
-
-
-/* Make a copy of PROPCHANGES (array of svn_prop_t) into *TRIMMED_PROPCHANGES,
-   omitting any svn:mergeinfo changes.  */
-static svn_error_t *
-omit_mergeinfo_changes(apr_array_header_t **trimmed_propchanges,
-                       const apr_array_header_t *propchanges,
-                       apr_pool_t *result_pool)
-{
-  int i;
-
-  *trimmed_propchanges = apr_array_make(result_pool,
-                                        propchanges->nelts,
-                                        sizeof(svn_prop_t));
-
-  for (i = 0; i < propchanges->nelts; ++i)
-    {
-      const svn_prop_t *change = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
-
-      /* If this property is not svn:mergeinfo, then copy it.  */
-      if (strcmp(change->name, SVN_PROP_MERGEINFO) != 0)
-        APR_ARRAY_PUSH(*trimmed_propchanges, svn_prop_t) = *change;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-
-/* Helper for merge_props_changed().
-
-   *PROPS is an array of svn_prop_t structures representing regular properties
-   to be added to the working copy TARGET_ABSPATH.
-
-   The merge source and target are assumed to be in the same repository.
-
-   Filter out mergeinfo property additions to TARGET_ABSPATH when
-   those additions refer to the same line of history as TARGET_ABSPATH as
-   described below.
-
-   Examine the added mergeinfo, looking at each range (or single rev)
-   of each source path.  If a source_path/range refers to the same line of
-   history as TARGET_ABSPATH (pegged at its base revision), then filter out
-   that range.  If the entire rangelist for a given path is filtered then
-   filter out the path as well.
-
-   RA_SESSION is an open RA session to the repository
-   in which both the source and target live, else RA_SESSION is not used. It
-   may be temporarily reparented as needed by this function.
-
-   Use CTX for any further client operations.
-
-   If any filtering occurs, set outgoing *PROPS to a shallow copy (allocated
-   in POOL) of incoming *PROPS minus the filtered mergeinfo. */
-static svn_error_t *
-filter_self_referential_mergeinfo(apr_array_header_t **props,
-                                  const char *target_abspath,
-                                  svn_ra_session_t *ra_session,
-                                  svn_client_ctx_t *ctx,
-                                  apr_pool_t *pool)
-{
-  apr_array_header_t *adjusted_props;
-  int i;
-  apr_pool_t *iterpool;
-  svn_boolean_t is_copy;
-  const char *repos_relpath;
-  svn_client__pathrev_t target_base;
-
-  /* If PATH itself has been added there is no need to filter. */
-  SVN_ERR(svn_wc__node_get_origin(&is_copy,  &target_base.rev, &repos_relpath,
-                                  &target_base.repos_root_url,
-                                  &target_base.repos_uuid, NULL, NULL,
-                                  ctx->wc_ctx, target_abspath, FALSE,
-                                  pool, pool));
-
-  if (is_copy || !repos_relpath)
-    return SVN_NO_ERROR; /* A copy or a local addition */
-
-  target_base.url = svn_path_url_add_component2(target_base.repos_root_url,
-                                                repos_relpath, pool);
-
-  adjusted_props = apr_array_make(pool, (*props)->nelts, sizeof(svn_prop_t));
-  iterpool = svn_pool_create(pool);
-  for (i = 0; i < (*props)->nelts; ++i)
-    {
-      svn_prop_t *prop = &APR_ARRAY_IDX((*props), i, svn_prop_t);
-
-      svn_mergeinfo_t mergeinfo, younger_mergeinfo;
-      svn_mergeinfo_t filtered_mergeinfo = NULL;
-      svn_mergeinfo_t filtered_younger_mergeinfo = NULL;
-      svn_error_t *err;
-
-      /* If this property isn't mergeinfo or is NULL valued (i.e. prop removal)
-         or empty mergeinfo it does not require any special handling.  There
-         is nothing to filter out of empty mergeinfo and the concept of
-         filtering doesn't apply if we are trying to remove mergeinfo
-         entirely.  */
-      if ((strcmp(prop->name, SVN_PROP_MERGEINFO) != 0)
-          || (! prop->value)       /* Removal of mergeinfo */
-          || (! prop->value->len)) /* Empty mergeinfo */
-        {
-          APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
-          continue;
-        }
-
-      svn_pool_clear(iterpool);
-
-      /* Non-empty mergeinfo; filter self-referential mergeinfo out. */
-
-      /* Parse the incoming mergeinfo to allow easier manipulation. */
-      err = svn_mergeinfo_parse(&mergeinfo, prop->value->data, iterpool);
-
-      if (err)
-        {
-          /* Issue #3896: If we can't parse it, we certainly can't
-             filter it. */
-          if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
-            {
-              svn_error_clear(err);
-              APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
-              continue;
-            }
-          else
-            {
-              return svn_error_trace(err);
-            }
-        }
-
-      /* The working copy target PATH is at BASE_REVISION.  Divide the
-         incoming mergeinfo into two groups.  One where all revision ranges
-         are as old or older than BASE_REVISION and one where all revision
-         ranges are younger.
-
-         Note: You may be wondering why we do this.
-
-         For the incoming mergeinfo "older" than target's base revision we
-         can filter out self-referential mergeinfo efficiently using
-         svn_client__get_history_as_mergeinfo().  We simply look at PATH's
-         natural history as mergeinfo and remove that from any incoming
-         mergeinfo.
-
-         For mergeinfo "younger" than the base revision we can't use
-         svn_ra_get_location_segments() to look into PATH's future
-         history.  Instead we must use svn_client__repos_locations() and
-         look at each incoming source/range individually and see if PATH
-         at its base revision and PATH at the start of the incoming range
-         exist on the same line of history.  If they do then we can filter
-         out the incoming range.  But since we have to do this for each
-         range there is a substantial performance penalty to pay if the
-         incoming ranges are not contiguous, i.e. we call
-         svn_client__repos_locations for each discrete range and incur
-         the cost of a roundtrip communication with the repository. */
-      SVN_ERR(split_mergeinfo_on_revision(&younger_mergeinfo,
-                                          &mergeinfo,
-                                          target_base.rev,
-                                          iterpool));
-
-      /* Filter self-referential mergeinfo from younger_mergeinfo. */
-      if (younger_mergeinfo)
-        {
-          apr_hash_index_t *hi;
-          const char *merge_source_root_url;
-
-          SVN_ERR(svn_ra_get_repos_root2(ra_session,
-                                         &merge_source_root_url, iterpool));
-
-          for (hi = apr_hash_first(iterpool, younger_mergeinfo);
-               hi; hi = apr_hash_next(hi))
-            {
-              int j;
-              const char *source_path = apr_hash_this_key(hi);
-              svn_rangelist_t *rangelist = apr_hash_this_val(hi);
-              const char *merge_source_url;
-              svn_rangelist_t *adjusted_rangelist =
-                apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
-
-              merge_source_url =
-                    svn_path_url_add_component2(merge_source_root_url,
-                                                source_path + 1, iterpool);
-
-              for (j = 0; j < rangelist->nelts; j++)
-                {
-                  svn_error_t *err2;
-                  svn_client__pathrev_t *start_loc;
-                  svn_merge_range_t *range =
-                    APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *);
-
-                  /* Because the merge source normalization code
-                     ensures mergeinfo refers to real locations on
-                     the same line of history, there's no need to
-                     look at the whole range, just the start. */
-
-                  /* Check if PATH@BASE_REVISION exists at
-                     RANGE->START on the same line of history.
-                     (start+1 because RANGE->start is not inclusive.) */
-                  err2 = svn_client__repos_location(&start_loc, ra_session,
-                                                    &target_base,
-                                                    range->start + 1,
-                                                    ctx, iterpool, iterpool);
-                  if (err2)
-                    {
-                      if (err2->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES
-                          || err2->apr_err == SVN_ERR_FS_NOT_FOUND
-                          || err2->apr_err == SVN_ERR_FS_NO_SUCH_REVISION)
-                        {
-                          /* PATH@BASE_REVISION didn't exist at
-                             RANGE->START + 1 or is unrelated to the
-                             resource PATH@RANGE->START.  Some of the
-                             requested revisions may not even exist in
-                             the repository; a real possibility since
-                             mergeinfo is hand editable.  In all of these
-                             cases clear and ignore the error and don't
-                             do any filtering.
-
-                             Note: In this last case it is possible that
-                             we will allow self-referential mergeinfo to
-                             be applied, but fixing it here is potentially
-                             very costly in terms of finding what part of
-                             a range is actually valid.  Simply allowing
-                             the merge to proceed without filtering the
-                             offending range seems the least worst
-                             option. */
-                          svn_error_clear(err2);
-                          err2 = NULL;
-                          APR_ARRAY_PUSH(adjusted_rangelist,
-                                         svn_merge_range_t *) = range;
-                        }
-                      else
-                        {
-                          return svn_error_trace(err2);
-                        }
-                     }
-                  else
-                    {
-                      /* PATH@BASE_REVISION exists on the same
-                         line of history at RANGE->START and RANGE->END.
-                         Now check that PATH@BASE_REVISION's path
-                         names at RANGE->START and RANGE->END are the same.
-                         If the names are not the same then the mergeinfo
-                         describing PATH@RANGE->START through
-                         PATH@RANGE->END actually belong to some other
-                         line of history and we want to record this
-                         mergeinfo, not filter it. */
-                      if (strcmp(start_loc->url, merge_source_url) != 0)
-                        {
-                          APR_ARRAY_PUSH(adjusted_rangelist,
-                                         svn_merge_range_t *) = range;
-                        }
-                    }
-                    /* else no need to add, this mergeinfo is
-                       all on the same line of history. */
-                } /* for (j = 0; j < rangelist->nelts; j++) */
-
-              /* Add any rangelists for source_path that are not
-                 self-referential. */
-              if (adjusted_rangelist->nelts)
-                {
-                  if (!filtered_younger_mergeinfo)
-                    filtered_younger_mergeinfo = apr_hash_make(iterpool);
-                  svn_hash_sets(filtered_younger_mergeinfo, source_path,
-                                adjusted_rangelist);
-                }
-
-            } /* Iteration over each merge source in younger_mergeinfo. */
-        } /* if (younger_mergeinfo) */
-
-      /* Filter self-referential mergeinfo from "older" mergeinfo. */
-      if (mergeinfo)
-        {
-          svn_mergeinfo_t implicit_mergeinfo;
-
-          SVN_ERR(svn_client__get_history_as_mergeinfo(
-            &implicit_mergeinfo, NULL,
-            &target_base, target_base.rev, SVN_INVALID_REVNUM,
-            ra_session, ctx, iterpool));
-
-          /* Remove PATH's implicit mergeinfo from the incoming mergeinfo. */
-          SVN_ERR(svn_mergeinfo_remove2(&filtered_mergeinfo,
-                                        implicit_mergeinfo,
-                                        mergeinfo, TRUE, iterpool, iterpool));
-        }
-
-      /* Combine whatever older and younger filtered mergeinfo exists
-         into filtered_mergeinfo. */
-      if (filtered_mergeinfo && filtered_younger_mergeinfo)
-        SVN_ERR(svn_mergeinfo_merge2(filtered_mergeinfo,
-                                     filtered_younger_mergeinfo, iterpool,
-                                     iterpool));
-      else if (filtered_younger_mergeinfo)
-        filtered_mergeinfo = filtered_younger_mergeinfo;
-
-      /* If there is any incoming mergeinfo remaining after filtering
-         then put it in adjusted_props. */
-      if (filtered_mergeinfo && apr_hash_count(filtered_mergeinfo))
-        {
-          /* Convert filtered_mergeinfo to a svn_prop_t and put it
-             back in the array. */
-          svn_string_t *filtered_mergeinfo_str;
-          svn_prop_t *adjusted_prop = apr_pcalloc(pool,
-                                                  sizeof(*adjusted_prop));
-          SVN_ERR(svn_mergeinfo_to_string(&filtered_mergeinfo_str,
-                                          filtered_mergeinfo,
-                                          pool));
-          adjusted_prop->name = SVN_PROP_MERGEINFO;
-          adjusted_prop->value = filtered_mergeinfo_str;
-          APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *adjusted_prop;
-        }
-    }
-  svn_pool_destroy(iterpool);
-
-  *props = adjusted_props;
-  return SVN_NO_ERROR;
-}
-
-/* Prepare a set of property changes PROPCHANGES to be used for a merge
-   operation on LOCAL_ABSPATH.
-
-   Remove all non-regular prop-changes (entry-props and WC-props).
-   Remove all non-mergeinfo prop-changes if it's a record-only merge.
-   Remove self-referential mergeinfo (### in some cases...)
-   Remove foreign-repository mergeinfo (### in some cases...)
-
-   Store the resulting property changes in *PROP_UPDATES.
-   Store information on where mergeinfo is updated in MERGE_B.
-
-   Used for both file and directory property merges. */
-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,
-                            apr_pool_t *result_pool,
-                            apr_pool_t *scratch_pool)
-{
-  apr_array_header_t *props;
-
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
-
-  /* We only want to merge "regular" version properties:  by
-     definition, 'svn merge' shouldn't touch any data within .svn/  */
-  SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
-                               result_pool));
-
-  /* If we are only applying mergeinfo changes then we need to do
-     additional filtering of PROPS so it contains only mergeinfo changes. */
-  if (merge_b->record_only && props->nelts)
-    {
-      apr_array_header_t *mergeinfo_props =
-        apr_array_make(result_pool, 1, sizeof(svn_prop_t));
-      int i;
-
-      for (i = 0; i < props->nelts; i++)
-        {
-          svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
-
-          if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
-            {
-              APR_ARRAY_PUSH(mergeinfo_props, svn_prop_t) = *prop;
-              break;
-            }
-        }
-      props = mergeinfo_props;
-    }
-
-  if (props->nelts)
-    {
-      /* Issue #3383: We don't want mergeinfo from a foreign repos.
-
-         If this is a merge from a foreign repository we must strip all
-         incoming mergeinfo (including mergeinfo deletions). */
-      if (! merge_b->same_repos)
-        SVN_ERR(omit_mergeinfo_changes(&props, props, result_pool));
-
-      /* If this is a forward merge then don't add new mergeinfo to
-         PATH that is already part of PATH's own history, see
-         http://svn.haxx.se/dev/archive-2008-09/0006.shtml.  If the
-         merge sources are not ancestral then there is no concept of a
-         'forward' or 'reverse' merge and we filter unconditionally. */
-      if (merge_b->merge_source.loc1->rev < merge_b->merge_source.loc2->rev
-          || !merge_b->merge_source.ancestral)
-        {
-          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));
-        }
-    }
-  *prop_updates = props;
-
-  /* Make a record in BATON if we find a PATH where mergeinfo is added
-     where none existed previously or PATH is having its existing
-     mergeinfo deleted. */
-  if (props->nelts)
-    {
-      int i;
-
-      for (i = 0; i < props->nelts; ++i)
-        {
-          svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
-
-          if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
-            {
-              /* Does LOCAL_ABSPATH have any pristine mergeinfo? */
-              svn_boolean_t has_pristine_mergeinfo = FALSE;
-              apr_hash_t *pristine_props;
-
-              SVN_ERR(svn_wc_get_pristine_props(&pristine_props,
-                                                merge_b->ctx->wc_ctx,
-                                                local_abspath,
-                                                scratch_pool,
-                                                scratch_pool));
-
-              if (pristine_props
-                  && svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
-                has_pristine_mergeinfo = TRUE;
-
-              if (!has_pristine_mergeinfo && prop->value)
-                {
-                  alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
-                                       local_abspath, merge_b->pool);
-                }
-              else if (has_pristine_mergeinfo && !prop->value)
-                {
-                  alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
-                                       local_abspath, merge_b->pool);
-                }
-            }
-        }
-    }
-
-  return SVN_NO_ERROR;
-}
-
-#define CONFLICT_REASON_NONE       ((svn_wc_conflict_reason_t)-1)
-#define CONFLICT_REASON_SKIP       ((svn_wc_conflict_reason_t)-2)
-#define CONFLICT_REASON_SKIP_WC    ((svn_wc_conflict_reason_t)-3)
-
-/* Baton used for testing trees for being editted while performing tree
-   conflict detection for incoming deletes */
-struct dir_delete_baton_t
-{
-  /* Reference to dir baton of directory that is the root of the deletion */
-  struct merge_dir_baton_t *del_root;
-
-  /* Boolean indicating that some edit is found. Allows avoiding more work */
-  svn_boolean_t found_edit;
-
-  /* A list of paths that are compared. Kept up to date until FOUND_EDIT is
-     set to TRUE */
-  apr_hash_t *compared_abspaths;
-};
-
-/* Baton for the merge_dir_*() functions. Initialized in merge_dir_opened() */
-struct merge_dir_baton_t
-{
-  /* Reference to the parent baton, unless the parent is the anchor, in which
-     case PARENT_BATON is NULL */
-  struct merge_dir_baton_t *parent_baton;
-
-  /* The pool containing this baton. Use for RESULT_POOL for storing in this
-     baton */
-  apr_pool_t *pool;
-
-  /* This directory doesn't have a representation in the working copy, so any
-     operation on it will be skipped and possibly cause a tree conflict on the
-     shadow root */
-  svn_boolean_t shadowed;
-
-  /* This node or one of its descendants received operational changes from the
-     merge. If this node is the shadow root its tree conflict status has been
-     applied */
-  svn_boolean_t edited;
-
-  /* If a tree conflict will be installed once edited, it's reason. If a skip
-     should be produced its reason. Otherwise CONFLICT_REASON_NONE for no tree
-     conflict.
-
-     Special values:
-       CONFLICT_REASON_SKIP:
-            The node will be skipped with content and property state as stored 
in
-            SKIP_REASON.
-
-       CONFLICT_REASON_SKIP_WC:
-            The node will be skipped as an obstructing working copy.
-   */
-  svn_wc_conflict_reason_t tree_conflict_reason;
-  svn_wc_conflict_action_t tree_conflict_action;
-  svn_node_kind_t tree_conflict_local_node_kind;
-  svn_node_kind_t tree_conflict_merge_left_node_kind;
-  svn_node_kind_t tree_conflict_merge_right_node_kind;
-
-  /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
-     add to the notification */
-  svn_wc_notify_state_t skip_reason;
-
-  /* TRUE if the node was added by this merge. Otherwise FALSE */
-  svn_boolean_t added;
-  svn_boolean_t add_is_replace; /* Add is second part of replace */
-
-  /* TRUE if we are taking over an existing directory as addition, otherwise
-     FALSE. */
-  svn_boolean_t add_existing;
-
-  /* NULL, or an hashtable mapping const char * local_abspaths to
-     const char *kind mapping, containing deleted nodes that still need a 
delete
-     notification (which may be a replaced notification if the node is not just
-     deleted) */
-  apr_hash_t *pending_deletes;
-
-  /* NULL, or an hashtable mapping const char * LOCAL_ABSPATHs to
-     a const svn_wc_conflict_description2_t * instance, describing the just
-     installed conflict */
-  apr_hash_t *new_tree_conflicts;
-
-  /* If not NULL, a reference to the information of the delete test that is
-     currently in progress. Allocated in the root-directory baton, referenced
-     from all descendants */
-  struct dir_delete_baton_t *delete_state;
-};
-
-/* Baton for the merge_dir_*() functions. Initialized in merge_file_opened() */
-struct merge_file_baton_t
-{
-  /* Reference to the parent baton, unless the parent is the anchor, in which
-     case PARENT_BATON is NULL */
-  struct merge_dir_baton_t *parent_baton;
-
-  /* This file doesn't have a representation in the working copy, so any
-     operation on it will be skipped and possibly cause a tree conflict
-     on the shadow root */
-  svn_boolean_t shadowed;
-
-  /* This node received operational changes from the merge. If this node
-     is the shadow root its tree conflict status has been applied */
-  svn_boolean_t edited;
-
-  /* If a tree conflict will be installed once edited, it's reason. If a skip
-     should be produced its reason. Some special values are defined. See the
-     merge_dir_baton_t for an explanation. */
-  svn_wc_conflict_reason_t tree_conflict_reason;
-  svn_wc_conflict_action_t tree_conflict_action;
-  svn_node_kind_t tree_conflict_local_node_kind;
-  svn_node_kind_t tree_conflict_merge_left_node_kind;
-  svn_node_kind_t tree_conflict_merge_right_node_kind;
-
-  /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
-     add to the notification */
-  svn_wc_notify_state_t skip_reason;
-
-  /* TRUE if the node was added by this merge. Otherwise FALSE */
-  svn_boolean_t added;
-  svn_boolean_t add_is_replace; /* Add is second part of replace */
-};
-
-/* Record the skip for future processing and (later) produce the
-   skip notification */
-static svn_error_t *
-record_skip(merge_cmd_baton_t *merge_b,
-            const char *local_abspath,
-            svn_node_kind_t kind,
-            svn_wc_notify_action_t action,
-            svn_wc_notify_state_t state,
-            struct merge_dir_baton_t *pdb,
-            apr_pool_t *scratch_pool)
-{
-  if (merge_b->record_only)
-    return SVN_NO_ERROR; /* ### Why? - Legacy compatibility */
-
-  if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
-      && !(pdb && pdb->shadowed))
-    {
-      store_path(merge_b->skipped_abspaths, local_abspath);
-    }
-
-  if (merge_b->notify_func)
-    {
-      svn_wc_notify_t *notify;
-
-      notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
-      notify->kind = kind;
-      notify->content_state = notify->prop_state = state;
-
-      merge_b->notify_func(merge_b->notify_baton, notify,
-                           scratch_pool);
-    }
-  return SVN_NO_ERROR;
-}
-
-/* Forward declaration */
-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);
-
-/* Record a tree conflict in the WC, unless this is a dry run or a record-
- * only merge, or if a tree conflict is already flagged for the VICTIM_PATH.
- * (The latter can happen if a merge-tracking-aware merge is doing multiple
- * editor drives because of a gap in the range of eligible revisions.)
- *
- * The tree conflict, with its victim specified by VICTIM_PATH, is
- * assumed to have happened during a merge using merge baton MERGE_B.
- *
- * ACTION and REASON correspond to the fields
- * of the same names in svn_wc_tree_conflict_description_t.
- */
-static svn_error_t *
-record_tree_conflict(merge_cmd_baton_t *merge_b,
-                     const char *local_abspath,
-                     struct merge_dir_baton_t *parent_baton,
-                     svn_node_kind_t local_node_kind,
-                     svn_node_kind_t merge_left_node_kind,
-                     svn_node_kind_t merge_right_node_kind,
-                     svn_wc_conflict_action_t action,
-                     svn_wc_conflict_reason_t reason,
-                     const svn_wc_conflict_description2_t *existing_conflict,
-                     svn_boolean_t notify_tc,
-                     apr_pool_t *scratch_pool)
-{
-  svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
-
-  if (merge_b->record_only)
-    return SVN_NO_ERROR;
-
-  if (merge_b->merge_source.ancestral
-      || merge_b->reintegrate_merge)
-    {
-      store_path(merge_b->tree_conflicted_abspaths, local_abspath);
-    }
-
-  alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
-                       merge_b->pool);
-
-  if (!merge_b->dry_run)
-    {
-       svn_wc_conflict_description2_t *conflict;
-       const svn_wc_conflict_version_t *left;
-       const svn_wc_conflict_version_t *right;
-       apr_pool_t *result_pool = parent_baton ? parent_baton->pool
-                                              : scratch_pool;
-
-      if (reason == svn_wc_conflict_reason_deleted)
-        {
-          const char *moved_to_abspath;
-
-          SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
-                                              wc_ctx, local_abspath,
-                                              scratch_pool, scratch_pool));
-
-          if (moved_to_abspath)
-            {
-              /* Local abspath itself has been moved away. If only a
-                 descendant is moved away, we call the node itself deleted */
-              reason = svn_wc_conflict_reason_moved_away;
-            }
-        }
-      else if (reason == svn_wc_conflict_reason_added)
-        {
-          const char *moved_from_abspath;
-          SVN_ERR(svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
-                                              wc_ctx, local_abspath,
-                                              scratch_pool, scratch_pool));
-          if (moved_from_abspath)
-            reason = svn_wc_conflict_reason_moved_here;
-        }
-
-      if (HONOR_MERGEINFO(merge_b) && merge_b->merge_source.ancestral)
-        {
-          struct svn_client__merge_source_t *source;
-          svn_client__pathrev_t *loc1;
-          svn_client__pathrev_t *loc2;
-          svn_merge_range_t range =
-            {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE};
-
-          /* We are honoring mergeinfo so do not blindly record
-           * a conflict describing the merge of
-           * SOURCE->LOC1->URL@SOURCE->LOC1->REV through
-           * SOURCE->LOC2->URL@SOURCE->LOC2->REV
-           * but figure out the actual revision range merged. */
-          (void)find_nearest_ancestor_with_intersecting_ranges(
-            &(range.start), &(range.end),
-            merge_b->children_with_mergeinfo,
-            action != svn_wc_conflict_action_delete,
-            local_abspath);
-          loc1 = svn_client__pathrev_dup(merge_b->merge_source.loc1,
-                                         scratch_pool);
-          loc2 = svn_client__pathrev_dup(merge_b->merge_source.loc2,
-                                         scratch_pool);
-          loc1->rev = range.start;
-          loc2->rev = range.end;
-          source = svn_client__merge_source_create(loc1, loc2,
-                                                   
merge_b->merge_source.ancestral,
-                                                   scratch_pool);
-          SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
-                                         merge_left_node_kind,
-                                         merge_right_node_kind,
-                                         source, merge_b->target,
-                                         result_pool, scratch_pool));
-        }
-      else
-        SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
-                                       merge_left_node_kind,
-                                       merge_right_node_kind,
-                                       &merge_b->merge_source, merge_b->target,
-                                       result_pool, scratch_pool));
-
-      /* Fix up delete of file, add of dir replacement (or other way around) */
-      if (existing_conflict != NULL && existing_conflict->src_left_version)
-          left = existing_conflict->src_left_version;
-
-      conflict = svn_wc_conflict_description_create_tree2(
-                        local_abspath, local_node_kind,
-                        svn_wc_operation_merge,
-                        left, right, result_pool);
-
-      conflict->action = action;
-      conflict->reason = reason;
-
-      /* May return SVN_ERR_WC_PATH_UNEXPECTED_STATUS */
-      if (existing_conflict)
-        SVN_ERR(svn_wc__del_tree_conflict(wc_ctx, local_abspath,
-                                          scratch_pool));
-
-      SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
-                                        scratch_pool));
-
-      if (parent_baton)
-        {
-          if (! parent_baton->new_tree_conflicts)
-            parent_baton->new_tree_conflicts = apr_hash_make(result_pool);
-
-          svn_hash_sets(parent_baton->new_tree_conflicts,
-                        apr_pstrdup(result_pool, local_abspath),
-                        conflict);
-        }
-
-      /* ### TODO: Store in parent baton */
-    }
-
-  /* On a replacement we currently get two tree conflicts */
-  if (merge_b->notify_func && notify_tc)
-    {
-      svn_wc_notify_t *notify;
-
-      notify = svn_wc_create_notify(local_abspath, svn_wc_notify_tree_conflict,
-                                    scratch_pool);
-      notify->kind = local_node_kind;
-
-      merge_b->notify_func(merge_b->notify_baton, notify,
-                           scratch_pool);
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Record the add for future processing and produce the
-   update_add notification
- */
-static svn_error_t *
-record_update_add(merge_cmd_baton_t *merge_b,
-                  const char *local_abspath,
-                  svn_node_kind_t kind,
-                  svn_boolean_t notify_replaced,
-                  apr_pool_t *scratch_pool)
-{
-  if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
-    {
-      store_path(merge_b->merged_abspaths, local_abspath);
-    }
-
-  if (merge_b->notify_func)
-    {
-      svn_wc_notify_t *notify;
-      svn_wc_notify_action_t action = svn_wc_notify_update_add;
-
-      if (notify_replaced)
-        action = svn_wc_notify_update_replace;
-
-      notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
-      notify->kind = kind;
-
-      merge_b->notify_func(merge_b->notify_baton, notify,
-                           scratch_pool);
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* 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,
-                     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 (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
-    {
-      store_path(merge_b->merged_abspaths, local_abspath);
-    }
-
-  if (merge_b->notify_func)
-    {
-      svn_wc_notify_t *notify;
-
-      notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_update,
-                                    scratch_pool);
-      notify->kind = kind;
-      notify->content_state = content_state;
-      notify->prop_state = prop_state;
-
-      merge_b->notify_func(merge_b->notify_baton, notify,
-                           scratch_pool);
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* 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,
-                     struct merge_dir_baton_t *parent_db,
-                     const char *local_abspath,
-                     svn_node_kind_t kind,
-                     apr_pool_t *scratch_pool)
-{
-  /* Update the lists of merged, skipped, tree-conflicted and added paths. */
-  if (merge_b->merge_source.ancestral
-      || merge_b->reintegrate_merge)
-    {
-      /* Issue #4166: If a previous merge added NOTIFY_ABSPATH, but we
-         are now deleting it, then remove it from the list of added
-         paths. */
-      svn_hash_sets(merge_b->added_abspaths, local_abspath, NULL);
-      store_path(merge_b->merged_abspaths, local_abspath);
-    }
-
-  if (parent_db)
-    {
-      const char *dup_abspath = apr_pstrdup(parent_db->pool, local_abspath);
-
-      if (!parent_db->pending_deletes)
-        parent_db->pending_deletes = apr_hash_make(parent_db->pool);
-
-      svn_hash_sets(parent_db->pending_deletes, dup_abspath,
-                    svn_node_kind_to_word(kind));
-    }
-
-  /* Note in children_with_mergeinfo that all paths in this subtree are
-   * being deleted, to avoid trying to set mergeinfo on them later. */
-  if (merge_b->children_with_mergeinfo)
-    {
-      int i;
-
-      for (i = 0; i < merge_b->children_with_mergeinfo->nelts; i++)
-        {
-          svn_client__merge_path_t *child
-            = APR_ARRAY_IDX(merge_b->children_with_mergeinfo, i,
-                            svn_client__merge_path_t *);
-
-          if (svn_dirent_is_ancestor(local_abspath, child->abspath))
-            {
-              
SVN_ERR(svn_sort__array_delete2(merge_b->children_with_mergeinfo, i--, 1));
-            }
-        }
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* 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,
-                             struct merge_dir_baton_t *db,
-                             apr_pool_t *scratch_pool)
-{
-  if (merge_b->notify_func && db->pending_deletes)
-    {
-      apr_hash_index_t *hi;
-
-      for (hi = apr_hash_first(scratch_pool, db->pending_deletes);
-           hi;
-           hi = apr_hash_next(hi))
-        {
-          const char *del_abspath = apr_hash_this_key(hi);
-          svn_wc_notify_t *notify;
-
-          notify = svn_wc_create_notify(del_abspath,
-                                        svn_wc_notify_update_delete,
-                                        scratch_pool);
-          notify->kind = svn_node_kind_from_word(
-                                    apr_hash_this_val(hi));
-
-          merge_b->notify_func(merge_b->notify_baton,
-                               notify, scratch_pool);
-        }
-
-      db->pending_deletes = NULL;
-    }
-  return SVN_NO_ERROR;
-}
-
-/* Helper function for the merge_dir_*() and merge_file_*() functions.
-
-   Installs and notifies pre-recorded tree conflicts and skips for
-   ancestors of operational merges
- */
-static svn_error_t *
-mark_dir_edited(merge_cmd_baton_t *merge_b,
-                struct merge_dir_baton_t *db,
-                const char *local_abspath,
-                apr_pool_t *scratch_pool)
-{
-  /* ### Too much common code with mark_file_edited */
-  if (db->edited)
-    return SVN_NO_ERROR;
-
-  if (db->parent_baton && !db->parent_baton->edited)
-    {
-      const char *dir_abspath = svn_dirent_dirname(local_abspath,
-                                                   scratch_pool);
-
-      SVN_ERR(mark_dir_edited(merge_b, db->parent_baton, dir_abspath,
-                              scratch_pool));
-    }
-
-  db->edited = TRUE;
-
-  if (! db->shadowed)
-    return SVN_NO_ERROR; /* Easy out */
-
-  if (db->parent_baton
-      && db->parent_baton->delete_state
-      && db->tree_conflict_reason != CONFLICT_REASON_NONE)
-    {
-      db->parent_baton->delete_state->found_edit = TRUE;
-    }
-  else if (db->tree_conflict_reason == CONFLICT_REASON_SKIP
-           || db->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
-    {
-      /* open_directory() decided not to flag a tree conflict, but
-         for clarity we produce a skip for this node that
-         most likely isn't touched by the merge itself */
-
-      if (merge_b->notify_func)
-        {
-          svn_wc_notify_t *notify;
-
-          notify = svn_wc_create_notify(
-                            local_abspath,
-                            (db->tree_conflict_reason == CONFLICT_REASON_SKIP)
-                                ? svn_wc_notify_skip
-                                : svn_wc_notify_update_skip_obstruction,
-                            scratch_pool);
-          notify->kind = svn_node_dir;
-          notify->content_state = notify->prop_state = db->skip_reason;
-
-          merge_b->notify_func(merge_b->notify_baton,
-                               notify,
-                               scratch_pool);
-        }
-
-      if (merge_b->merge_source.ancestral
-          || merge_b->reintegrate_merge)
-        {
-          store_path(merge_b->skipped_abspaths, local_abspath);
-        }
-    }
-  else if (db->tree_conflict_reason != CONFLICT_REASON_NONE)
-    {
-      /* open_directory() decided that a tree conflict should be raised */
-
-      SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
-                                   db->tree_conflict_local_node_kind,
-                                   db->tree_conflict_merge_left_node_kind,
-                                   db->tree_conflict_merge_right_node_kind,
-                                   db->tree_conflict_action,
-                                   db->tree_conflict_reason,
-                                   NULL, TRUE,
-                                   scratch_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Helper function for the merge_file_*() functions.
-
-   Installs and notifies pre-recorded tree conflicts and skips for
-   ancestors of operational merges
- */
-static svn_error_t *
-mark_file_edited(merge_cmd_baton_t *merge_b,
-                 struct merge_file_baton_t *fb,
-                 const char *local_abspath,
-                 apr_pool_t *scratch_pool)
-{
-  /* ### Too much common code with mark_dir_edited */
-  if (fb->edited)
-    return SVN_NO_ERROR;
-
-  if (fb->parent_baton && !fb->parent_baton->edited)
-    {
-      const char *dir_abspath = svn_dirent_dirname(local_abspath,
-                                                   scratch_pool);
-
-      SVN_ERR(mark_dir_edited(merge_b, fb->parent_baton, dir_abspath,
-                              scratch_pool));
-    }
-
-  fb->edited = TRUE;
-
-  if (! fb->shadowed)
-    return SVN_NO_ERROR; /* Easy out */
-
-  if (fb->parent_baton
-      && fb->parent_baton->delete_state
-      && fb->tree_conflict_reason != CONFLICT_REASON_NONE)
-    {
-      fb->parent_baton->delete_state->found_edit = TRUE;
-    }
-  else if (fb->tree_conflict_reason == CONFLICT_REASON_SKIP
-           || fb->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
-    {
-      /* open_directory() decided not to flag a tree conflict, but
-         for clarity we produce a skip for this node that
-         most likely isn't touched by the merge itself */
-
-      if (merge_b->notify_func)
-        {
-          svn_wc_notify_t *notify;
-
-          notify = svn_wc_create_notify(local_abspath, svn_wc_notify_skip,
-                                        scratch_pool);
-          notify->kind = svn_node_file;
-          notify->content_state = notify->prop_state = fb->skip_reason;
-
-          merge_b->notify_func(merge_b->notify_baton,
-                               notify,
-                               scratch_pool);
-        }
-
-      if (merge_b->merge_source.ancestral
-          || merge_b->reintegrate_merge)
-        {
-          store_path(merge_b->skipped_abspaths, local_abspath);
-        }
-    }
-  else if (fb->tree_conflict_reason != CONFLICT_REASON_NONE)
-    {
-      /* open_file() decided that a tree conflict should be raised */
-
-      SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
-                                   fb->tree_conflict_local_node_kind,
-                                   fb->tree_conflict_merge_left_node_kind,
-                                   fb->tree_conflict_merge_right_node_kind,
-                                   fb->tree_conflict_action,
-                                   fb->tree_conflict_reason,
-                                   NULL, TRUE,
-                                   scratch_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_diff_tree_processor_t function.
-
-   Called before either merge_file_changed(), merge_file_added(),
-   merge_file_deleted() or merge_file_closed(), unless it sets *SKIP to TRUE.
-
-   When *SKIP is TRUE, the diff driver avoids work on getting the details
-   for the closing callbacks.
- */
-static svn_error_t *
-merge_file_opened(void **new_file_baton,
-                  svn_boolean_t *skip,
-                  const char *relpath,
-                  const svn_diff_source_t *left_source,
-                  const svn_diff_source_t *right_source,
-                  const svn_diff_source_t *copyfrom_source,
-                  void *dir_baton,
-                  const struct svn_diff_tree_processor_t *processor,
-                  apr_pool_t *result_pool,
-                  apr_pool_t *scratch_pool)
-{
-  merge_cmd_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,
-                                              relpath, scratch_pool);
-
-  fb = apr_pcalloc(result_pool, sizeof(*fb));
-  fb->tree_conflict_reason = CONFLICT_REASON_NONE;
-  fb->tree_conflict_action = svn_wc_conflict_action_edit;
-  fb->skip_reason = svn_wc_notify_state_unknown;
-
-  if (left_source)
-    fb->tree_conflict_merge_left_node_kind = svn_node_file;
-  else
-    fb->tree_conflict_merge_left_node_kind = svn_node_none;
-
-  if (right_source)
-    fb->tree_conflict_merge_right_node_kind = svn_node_file;
-  else
-    fb->tree_conflict_merge_right_node_kind = svn_node_none;
-
-  *new_file_baton = fb;
-
-  if (pdb)
-    {
-      fb->parent_baton = pdb;
-      fb->shadowed = pdb->shadowed;
-      fb->skip_reason = pdb->skip_reason;
-    }
-
-  if (fb->shadowed)
-    {
-      /* An ancestor is tree conflicted. Nothing to do here. */
-    }
-  else if (left_source != NULL)
-    {
-      /* Node is expected to be a file, which will be changed or deleted. */
-      svn_boolean_t is_deleted;
-      svn_boolean_t excluded;
-      svn_depth_t parent_depth;
-
-      if (! right_source)
-        fb->tree_conflict_action = svn_wc_conflict_action_delete;
-
-      {
-        svn_wc_notify_state_t obstr_state;
-
-        SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
-                                          &fb->tree_conflict_local_node_kind,
-                                          &parent_depth,
-                                          merge_b, local_abspath,
-                                          scratch_pool));
-
-        if (obstr_state != svn_wc_notify_state_inapplicable)
-          {
-            fb->shadowed = TRUE;
-            fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
-            fb->skip_reason = obstr_state;
-            return SVN_NO_ERROR;
-          }
-
-        if (is_deleted)
-          fb->tree_conflict_local_node_kind = svn_node_none;
-      }
-
-      if (fb->tree_conflict_local_node_kind == svn_node_none)
-        {
-          fb->shadowed = TRUE;
-
-          /* If this is not the merge target and the parent is too shallow to
-             contain this directory, and the directory is not present
-             via exclusion or depth filtering, skip it instead of recording
-             a tree conflict.
-
-             Non-inheritable mergeinfo will be recorded, allowing
-             future merges into non-shallow working copies to merge
-             changes we missed this time around. */
-          if (pdb && (excluded
-                      || (parent_depth != svn_depth_unknown &&
-                          parent_depth < svn_depth_files)))
-            {
-                fb->shadowed = TRUE;
-
-                fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
-                fb->skip_reason = svn_wc_notify_state_missing;
-                return SVN_NO_ERROR;
-            }
-
-          if (is_deleted)
-            fb->tree_conflict_reason = svn_wc_conflict_reason_deleted;
-          else
-            fb->tree_conflict_reason = svn_wc_conflict_reason_missing;
-
-          /* ### Similar to directory */
-          *skip = TRUE;
-          SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
-          return SVN_NO_ERROR;
-          /* ### /Similar */
-        }
-      else if (fb->tree_conflict_local_node_kind != svn_node_file)
-        {
-          svn_boolean_t added;
-          fb->shadowed = TRUE;
-
-          SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
-                                        local_abspath, scratch_pool));
-
-          fb->tree_conflict_reason = added ? svn_wc_conflict_reason_added
-                                           : svn_wc_conflict_reason_obstructed;
-
-          /* ### Similar to directory */
-          *skip = TRUE;
-          SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
-          return SVN_NO_ERROR;
-          /* ### /Similar */
-        }
-
-      if (! right_source)
-        {
-          /* We want to delete the directory */
-          fb->tree_conflict_action = svn_wc_conflict_action_delete;
-          SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
-
-          if (fb->shadowed)
-            {
-              return SVN_NO_ERROR; /* Already set a tree conflict */
-            }
-
-          /* Comparison mode to verify for delete tree conflicts? */
-          if (pdb && pdb->delete_state
-              && pdb->delete_state->found_edit)
-            {
-              /* Earlier nodes found a conflict. Done. */
-              *skip = TRUE;
-            }
-        }
-    }
-  else
-    {
-      const svn_wc_conflict_description2_t *old_tc = NULL;
-
-      /* The node doesn't exist pre-merge: We have an addition */
-      fb->added = TRUE;
-      fb->tree_conflict_action = svn_wc_conflict_action_add;
-
-      if (pdb && pdb->pending_deletes
-          && svn_hash_gets(pdb->pending_deletes, local_abspath))
-        {
-          fb->add_is_replace = TRUE;
-          fb->tree_conflict_action = svn_wc_conflict_action_replace;
-
-          svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
-        }
-
-      if (pdb
-          && pdb->new_tree_conflicts
-          && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
-        {
-          fb->tree_conflict_action = svn_wc_conflict_action_replace;
-          fb->tree_conflict_reason = old_tc->reason;
-
-          /* Update the tree conflict to store that this is a replace */
-          SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
-                                       old_tc->node_kind,
-                                       old_tc->src_left_version->node_kind,
-                                       svn_node_file,
-                                       fb->tree_conflict_action,
-                                       fb->tree_conflict_reason,
-                                       old_tc, FALSE,
-                                       scratch_pool));
-
-          if (old_tc->reason == svn_wc_conflict_reason_deleted
-              || old_tc->reason == svn_wc_conflict_reason_moved_away)
-            {
-              /* Issue #3806: Incoming replacements on local deletes produce
-                 inconsistent result.
-
-                 In this specific case we can continue applying the add part
-                 of the replacement. */
-            }
-          else
-            {
-              *skip = TRUE;
-
-              return SVN_NO_ERROR;
-            }
-        }
-      else if (! (merge_b->dry_run
-                  && ((pdb && pdb->added) || fb->add_is_replace)))
-        {
-          svn_wc_notify_state_t obstr_state;
-          svn_boolean_t is_deleted;
-
-          SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
-                                            &fb->tree_conflict_local_node_kind,
-                                            NULL, merge_b, local_abspath,
-                                            scratch_pool));
-
-          if (obstr_state != svn_wc_notify_state_inapplicable)
-            {
-              /* Skip the obstruction */
-              fb->shadowed = TRUE;
-              fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
-              fb->skip_reason = obstr_state;
-            }
-          else if (fb->tree_conflict_local_node_kind != svn_node_none
-                   && !is_deleted)
-            {
-              /* Set a tree conflict */
-              svn_boolean_t added;
-
-              fb->shadowed = TRUE;
-              SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
-                                            local_abspath, scratch_pool));
-
-              fb->tree_conflict_reason = added ? svn_wc_conflict_reason_added
-                                               : 
svn_wc_conflict_reason_obstructed;
-            }
-        }
-
-      /* Handle pending conflicts */
-      SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_diff_tree_processor_t function.
- *
- * Called after merge_file_opened() when a node receives only text and/or
- * property changes between LEFT_SOURCE and RIGHT_SOURCE.
- *
- * left_file and right_file can be NULL when the file is not modified.
- * left_props and right_props are always available.
- */
-static svn_error_t *
-merge_file_changed(const char *relpath,
-                  const svn_diff_source_t *left_source,
-                  const svn_diff_source_t *right_source,
-                  const char *left_file,
-                  const char *right_file,
-                  /*const*/ apr_hash_t *left_props,
-                  /*const*/ apr_hash_t *right_props,
-                  svn_boolean_t file_modified,
-                  const apr_array_header_t *prop_changes,
-                  void *file_baton,
-                  const struct svn_diff_tree_processor_t *processor,
-                  apr_pool_t *scratch_pool)
-{
-  merge_cmd_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,
-                                              relpath, scratch_pool);
-  const svn_wc_conflict_version_t *left;
-  const svn_wc_conflict_version_t *right;
-  svn_wc_notify_state_t text_state;
-  svn_wc_notify_state_t property_state;
-
-  SVN_ERR_ASSERT(local_abspath && svn_dirent_is_absolute(local_abspath));
-  SVN_ERR_ASSERT(!left_file || svn_dirent_is_absolute(left_file));
-  SVN_ERR_ASSERT(!right_file || svn_dirent_is_absolute(right_file));
-
-  SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
-
-  if (fb->shadowed)
-    {
-      if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
-        {
-          /* We haven't notified for this node yet: report a skip */
-          SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
-                              svn_wc_notify_update_shadowed_update,
-                              fb->skip_reason, fb->parent_baton,
-                              scratch_pool));
-        }
-
-      return SVN_NO_ERROR;
-    }
-
-  /* This callback is essentially no more than a wrapper around
-     svn_wc_merge6().  Thank goodness that all the
-     diff-editor-mechanisms are doing the hard work of getting the
-     fulltexts! */
-
-  property_state = svn_wc_notify_state_unchanged;
-  text_state = svn_wc_notify_state_unchanged;
-
-  SVN_ERR(prepare_merge_props_changed(&prop_changes, local_abspath,
-                                      prop_changes, merge_b,
-                                      scratch_pool, scratch_pool));
-
-  SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
-                                 svn_node_file, svn_node_file,
-                                 &merge_b->merge_source, merge_b->target,
-                                 scratch_pool, scratch_pool));
-
-  /* Do property merge now, if we are not going to perform a text merge */
-  if ((merge_b->record_only || !left_file) && prop_changes->nelts)
-    {
-      SVN_ERR(svn_wc_merge_props3(&property_state, ctx->wc_ctx, local_abspath,
-                                  left, right,
-                                  left_props, prop_changes,
-                                  merge_b->dry_run,
-                                  NULL, NULL,
-                                  ctx->cancel_func, ctx->cancel_baton,
-                                  scratch_pool));
-      if (property_state == svn_wc_notify_state_conflicted)
-        {
-          alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
-                               merge_b->pool);
-        }
-    }
-
-  /* Easy out: We are only applying mergeinfo differences. */
-  if (merge_b->record_only)
-    {
-      /* NO-OP */
-    }
-  else if (left_file)
-    {
-      svn_boolean_t has_local_mods;
-      enum svn_wc_merge_outcome_t content_outcome;
-      const char *target_label;
-      const char *left_label;
-      const char *right_label;
-      const char *path_ext = "";
-
-      if (merge_b->ext_patterns && merge_b->ext_patterns->nelts)
-        {
-          svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
-          if (! (*path_ext
-                 && svn_cstring_match_glob_list(path_ext,
-                                                merge_b->ext_patterns)))
-            {
-              path_ext = "";
-            }
-        }
-
-      /* xgettext: the '.working', '.merge-left.r%ld' and
-         '.merge-right.r%ld' strings are used to tag onto a file
-         name in case of a merge conflict */
-
-      target_label = apr_psprintf(scratch_pool, _(".working%s%s"),
-                                  *path_ext ? "." : "", path_ext);
-      left_label = apr_psprintf(scratch_pool,
-                                _(".merge-left.r%ld%s%s"),
-                                left_source->revision,
-                                *path_ext ? "." : "", path_ext);
-      right_label = apr_psprintf(scratch_pool,
-                                 _(".merge-right.r%ld%s%s"),
-                                 right_source->revision,
-                                 *path_ext ? "." : "", path_ext);
-
-      SVN_ERR(svn_wc_text_modified_p2(&has_local_mods, ctx->wc_ctx,
-                                      local_abspath, FALSE, scratch_pool));
-
-      /* Do property merge and text merge in one step so that keyword expansion
-         takes into account the new property values. */
-      SVN_ERR(svn_wc_merge6(&content_outcome, &property_state, ctx->wc_ctx,
-                            left_file, right_file, local_abspath,
-                            left_label, right_label, target_label,
-                            left, right,
-                            merge_b->dry_run, merge_b->diff3_cmd,
-                            merge_b->merge_options,
-                            left_props, prop_changes,
-                            NULL, NULL,
-                            ctx->cancel_func,
-                            ctx->cancel_baton,
-                            scratch_pool));
-
-      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);
-        }
-
-      if (content_outcome == svn_wc_merge_conflict)
-        text_state = svn_wc_notify_state_conflicted;
-      else if (has_local_mods
-               && content_outcome != svn_wc_merge_unchanged)
-        text_state = svn_wc_notify_state_merged;
-      else if (content_outcome == svn_wc_merge_merged)
-        text_state = svn_wc_notify_state_changed;
-      else if (content_outcome == svn_wc_merge_no_merge)
-        text_state = svn_wc_notify_state_missing;
-      else /* merge_outcome == svn_wc_merge_unchanged */
-        text_state = svn_wc_notify_state_unchanged;
-    }
-
-  if (text_state == svn_wc_notify_state_conflicted
-      || text_state == svn_wc_notify_state_merged
-      || text_state == svn_wc_notify_state_changed
-      || property_state == svn_wc_notify_state_conflicted
-      || property_state == svn_wc_notify_state_merged
-      || property_state == svn_wc_notify_state_changed)
-    {
-      SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
-                                   text_state, property_state,
-                                   scratch_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_diff_tree_processor_t function.
- *
- * Called after merge_file_opened() when a node doesn't exist in LEFT_SOURCE,
- * but does in RIGHT_SOURCE.
- *
- * When a node is replaced instead of just added a separate opened+deleted will
- * be invoked before the current open+added.
- */
-static svn_error_t *
-merge_file_added(const char *relpath,
-                 const svn_diff_source_t *copyfrom_source,
-                 const svn_diff_source_t *right_source,
-                 const char *copyfrom_file,
-                 const char *right_file,
-                 /*const*/ apr_hash_t *copyfrom_props,
-                 /*const*/ apr_hash_t *right_props,
-                 void *file_baton,
-                 const struct svn_diff_tree_processor_t *processor,
-                 apr_pool_t *scratch_pool)
-{
-  merge_cmd_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);
-  apr_hash_t *pristine_props;
-  apr_hash_t *new_props;
-
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
-
-  SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
-
-  if (fb->shadowed)
-    {
-      if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
-        {
-          /* We haven't notified for this node yet: report a skip */
-          SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
-                              svn_wc_notify_update_shadowed_add,
-                              fb->skip_reason, fb->parent_baton,
-                              scratch_pool));
-        }
-
-      return SVN_NO_ERROR;
-    }
-
-  /* Easy out: We are only applying mergeinfo differences. */
-  if (merge_b->record_only)
-    {
-      return SVN_NO_ERROR;
-    }
-
-  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);
-    }
-
-  if (!merge_b->dry_run)
-    {
-      const char *copyfrom_url;
-      svn_revnum_t copyfrom_rev;
-      svn_stream_t *new_contents, *pristine_contents;
-
-      /* If this is a merge from the same repository as our
-         working copy, we handle adds as add-with-history.
-         Otherwise, we'll use a pure add. */
-      if (merge_b->same_repos)
-        {
-          copyfrom_url = svn_path_url_add_component2(
-                                       merge_b->merge_source.loc2->url,
-                                       relpath, scratch_pool);
-          copyfrom_rev = right_source->revision;
-          SVN_ERR(check_repos_match(merge_b->target, local_abspath,
-                                    copyfrom_url, scratch_pool));
-          SVN_ERR(svn_stream_open_readonly(&pristine_contents,
-                                           right_file,
-                                           scratch_pool,
-                                           scratch_pool));
-          new_contents = NULL; /* inherit from new_base_contents */
-
-          pristine_props = right_props; /* Includes last_* information */
-          new_props = NULL; /* No local changes */
-
-          if (svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
-            {
-              alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
-                                   local_abspath, merge_b->pool);
-            }
-        }
-      else
-        {
-          apr_array_header_t *regular_props;
-
-          copyfrom_url = NULL;
-          copyfrom_rev = SVN_INVALID_REVNUM;
-
-          pristine_contents = svn_stream_empty(scratch_pool);
-          SVN_ERR(svn_stream_open_readonly(&new_contents, right_file,
-                                           scratch_pool, scratch_pool));
-
-          pristine_props = apr_hash_make(scratch_pool); /* Local addition */
-
-          /* We don't want any foreign properties */
-          SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props,
-                                                              scratch_pool),
-                                       NULL, NULL, &regular_props,
-                                       scratch_pool));
-
-          new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
-
-          /* Issue #3383: We don't want mergeinfo from a foreign repository. */
-          svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
-        }
-
-      /* Do everything like if we had called 'svn cp PATH1 PATH2'. */
-      SVN_ERR(svn_wc_add_repos_file4(merge_b->ctx->wc_ctx,
-                                      local_abspath,
-                                      pristine_contents,
-                                      new_contents,
-                                      pristine_props, new_props,
-                                      copyfrom_url, copyfrom_rev,
-                                      merge_b->ctx->cancel_func,
-                                      merge_b->ctx->cancel_baton,
-                                      scratch_pool));
-
-      /* Caller must call svn_sleep_for_timestamps() */
-      *merge_b->use_sleep = TRUE;
-    }
-
-  SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_file,
-                            fb->add_is_replace, scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
-/* Compare the two sets of properties PROPS1 and PROPS2, ignoring the
- * "svn:mergeinfo" property, and noticing only "normal" props. Set *SAME to
- * true if the rest of the properties are identical or false if they differ.
- */
-static svn_error_t *
-properties_same_p(svn_boolean_t *same,
-                  apr_hash_t *props1,
-                  apr_hash_t *props2,
-                  apr_pool_t *scratch_pool)
-{
-  apr_array_header_t *prop_changes;
-  int i, diffs;
-
-  /* Examine the properties that differ */
-  SVN_ERR(svn_prop_diffs(&prop_changes, props1, props2, scratch_pool));
-  diffs = 0;
-  for (i = 0; i < prop_changes->nelts; i++)
-    {
-      const char *pname = APR_ARRAY_IDX(prop_changes, i, svn_prop_t).name;
-
-      /* Count the properties we're interested in; ignore the rest */
-      if (svn_wc_is_normal_prop(pname)
-          && strcmp(pname, SVN_PROP_MERGEINFO) != 0)
-        diffs++;
-    }
-  *same = (diffs == 0);
-  return SVN_NO_ERROR;
-}
-
-/* Compare the file OLDER_ABSPATH (together with its normal properties in
- * ORIGINAL_PROPS which may also contain WC props and entry props) with the
- * versioned file MINE_ABSPATH (together with its versioned properties).
- * Set *SAME to true if they are the same or false if they differ, ignoring
- * the "svn:mergeinfo" property, and ignoring differences in keyword
- * expansion and end-of-line style. */
-static svn_error_t *
-files_same_p(svn_boolean_t *same,
-             const char *older_abspath,
-             apr_hash_t *original_props,
-             const char *mine_abspath,
-             svn_wc_context_t *wc_ctx,
-             apr_pool_t *scratch_pool)
-{
-  apr_hash_t *working_props;
-
-  SVN_ERR(svn_wc_prop_list2(&working_props, wc_ctx, mine_abspath,
-                            scratch_pool, scratch_pool));
-
-  /* Compare the properties */
-  SVN_ERR(properties_same_p(same, original_props, working_props,
-                            scratch_pool));
-  if (*same)
-    {
-      svn_stream_t *mine_stream;
-      svn_stream_t *older_stream;
-      svn_string_t *special = svn_hash_gets(working_props, SVN_PROP_SPECIAL);
-      svn_string_t *eol_style = svn_hash_gets(working_props, 
SVN_PROP_EOL_STYLE);
-      svn_string_t *keywords = svn_hash_gets(working_props, SVN_PROP_KEYWORDS);
-
-      /* Compare the file content, translating 'mine' to 'normal' form. */
-      if (special != NULL)
-        SVN_ERR(svn_subst_read_specialfile(&mine_stream, mine_abspath,
-                                           scratch_pool, scratch_pool));
-      else
-        SVN_ERR(svn_stream_open_readonly(&mine_stream, mine_abspath,
-                                         scratch_pool, scratch_pool));
-
-      if (!special && (eol_style || keywords))
-        {
-          apr_hash_t *kw = NULL;
-          const char *eol = NULL;
-          svn_subst_eol_style_t style;
-
-          /* We used to use svn_client__get_normalized_stream() here, but
-             that doesn't work in 100% of the cases because it doesn't
-             convert EOLs to the repository form; just to '\n'.
-           */
-
-          if (eol_style)
-            {
-              svn_subst_eol_style_from_value(&style, &eol, eol_style->data);
-
-              if (style == svn_subst_eol_style_native)
-                eol = SVN_SUBST_NATIVE_EOL_STR;
-              else if (style != svn_subst_eol_style_fixed
-                       && style != svn_subst_eol_style_none)
-                return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL);
-            }
-
-          if (keywords)
-            SVN_ERR(svn_subst_build_keywords3(&kw, keywords->data, "", "",
-                                              "", 0, "", scratch_pool));
-
-          mine_stream = svn_subst_stream_translated(
-            mine_stream, eol, FALSE, kw, FALSE, scratch_pool);
-        }
-
-      SVN_ERR(svn_stream_open_readonly(&older_stream, older_abspath,
-                                       scratch_pool, scratch_pool));
-
-      SVN_ERR(svn_stream_contents_same2(same, mine_stream, older_stream,
-                                        scratch_pool));
-
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_diff_tree_processor_t function.
- *
- * Called after merge_file_opened() when a node does exist in LEFT_SOURCE, but
- * no longer exists (or is replaced) in RIGHT_SOURCE.
- *
- * When a node is replaced instead of just added a separate opened+added will
- * be invoked after the current open+deleted.
- */
-static svn_error_t *
-merge_file_deleted(const char *relpath,
-                   const svn_diff_source_t *left_source,
-                   const char *left_file,
-                   /*const*/ apr_hash_t *left_props,
-                   void *file_baton,
-                   const struct svn_diff_tree_processor_t *processor,
-                   apr_pool_t *scratch_pool)
-{
-  merge_cmd_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);
-  svn_boolean_t same;
-
-  SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
-
-  if (fb->shadowed)
-    {
-      if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
-        {
-          /* We haven't notified for this node yet: report a skip */
-          SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
-                              svn_wc_notify_update_shadowed_delete,
-                              fb->skip_reason, fb->parent_baton,
-                              scratch_pool));
-        }
-
-      return SVN_NO_ERROR;
-    }
-
-  /* Easy out: We are only applying mergeinfo differences. */
-  if (merge_b->record_only)
-    {
-      return SVN_NO_ERROR;
-    }
-
-  /* If the files are identical, attempt deletion */
-  if (merge_b->force_delete)
-    same = TRUE;
-  else
-    SVN_ERR(files_same_p(&same, left_file, left_props,
-                         local_abspath, merge_b->ctx->wc_ctx,
-                         scratch_pool));
-
-  if (fb->parent_baton
-      && fb->parent_baton->delete_state)
-    {
-      if (same)
-        {
-          /* Note that we checked this file */
-          store_path(fb->parent_baton->delete_state->compared_abspaths,
-                     local_abspath);
-        }
-      else
-        {
-          /* We found some modification. Parent should raise a tree conflict */
-          fb->parent_baton->delete_state->found_edit = TRUE;
-        }
-
-      return SVN_NO_ERROR;
-    }
-  else if (same)
-    {
-      if (!merge_b->dry_run)
-        SVN_ERR(svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
-                               FALSE /* keep_local */, FALSE /* unversioned */,
-                               merge_b->ctx->cancel_func,
-                               merge_b->ctx->cancel_baton,
-                               NULL, NULL /* no notify */,
-                               scratch_pool));
-
-      /* Record that we might have deleted mergeinfo */
-      alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
-                           local_abspath, merge_b->pool);
-
-      /* And notify the deletion */
-      SVN_ERR(record_update_delete(merge_b, fb->parent_baton, local_abspath,
-                                   svn_node_file, scratch_pool));
-    }
-  else
-    {
-      /* The files differ, so raise a conflict instead of deleting */
-
-      /* This is use case 5 described in the paper attached to issue
-       * #2282.  See also notes/tree-conflicts/detection.txt
-       */
-      SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
-                                   svn_node_file,
-                                   svn_node_file,
-                                   svn_node_none,
-                                   svn_wc_conflict_action_delete,
-                                   svn_wc_conflict_reason_edited,
-                                   NULL, TRUE,
-                                   scratch_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* An svn_diff_tree_processor_t function.
-
-   Called before either merge_dir_changed(), merge_dir_added(),
-   merge_dir_deleted() or merge_dir_closed(), unless it sets *SKIP to TRUE.
-
-   After this call and before the close call, all descendants will receive
-   their changes, unless *SKIP_CHILDREN is set to TRUE.
-
-   When *SKIP is TRUE, the diff driver avoids work on getting the details
-   for the closing callbacks.
-
-   The SKIP and SKIP_DESCENDANTS work independently.
- */
-static svn_error_t *
-merge_dir_opened(void **new_dir_baton,
-                 svn_boolean_t *skip,
-                 svn_boolean_t *skip_children,
-                 const char *relpath,
-                 const svn_diff_source_t *left_source,
-                 const svn_diff_source_t *right_source,
-                 const svn_diff_source_t *copyfrom_source,
-                 void *parent_dir_baton,
-                 const struct svn_diff_tree_processor_t *processor,
-                 apr_pool_t *result_pool,
-                 apr_pool_t *scratch_pool)
-{
-  merge_cmd_baton_t *merge_b = processor->baton;
-  struct merge_dir_baton_t *db;
-  struct merge_dir_baton_t *pdb = parent_dir_baton;
-
-  const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
-                                              relpath, scratch_pool);
-
-  db = apr_pcalloc(result_pool, sizeof(*db));
-  db->pool = result_pool;
-  db->tree_conflict_reason = CONFLICT_REASON_NONE;
-  db->tree_conflict_action = svn_wc_conflict_action_edit;
-  db->skip_reason = svn_wc_notify_state_unknown;
-
-  *new_dir_baton = db;
-
-  if (left_source)
-    db->tree_conflict_merge_left_node_kind = svn_node_dir;
-  else
-    db->tree_conflict_merge_left_node_kind = svn_node_none;
-
-  if (right_source)
-    db->tree_conflict_merge_right_node_kind = svn_node_dir;
-  else
-    db->tree_conflict_merge_right_node_kind = svn_node_none;
-
-  if (pdb)
-    {
-      db->parent_baton = pdb;
-      db->shadowed = pdb->shadowed;
-      db->skip_reason = pdb->skip_reason;
-    }
-
-  if (db->shadowed)
-    {
-      /* An ancestor is tree conflicted. Nothing to do here. */
-      if (! left_source)
-        db->added = TRUE;
-    }
-  else if (left_source != NULL)
-    {
-      /* Node is expected to be a directory. */
-      svn_boolean_t is_deleted;
-      svn_boolean_t excluded;
-      svn_depth_t parent_depth;
-
-      if (! right_source)
-          db->tree_conflict_action = svn_wc_conflict_action_delete;
-
-      /* Check for an obstructed or missing node on disk. */
-      {
-        svn_wc_notify_state_t obstr_state;
-        SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
-                                          &db->tree_conflict_local_node_kind,
-                                          &parent_depth, merge_b,
-                                          local_abspath, scratch_pool));
-
-        if (obstr_state != svn_wc_notify_state_inapplicable)
-          {
-            db->shadowed = TRUE;
-
-            if (obstr_state == svn_wc_notify_state_obstructed)
-              {
-                svn_boolean_t is_wcroot;
-
-                SVN_ERR(svn_wc_check_root(&is_wcroot, NULL, NULL,
-                                        merge_b->ctx->wc_ctx,
-                                        local_abspath, scratch_pool));
-
-                if (is_wcroot)
-                  {
-                    db->tree_conflict_reason = CONFLICT_REASON_SKIP_WC;
-                    return SVN_NO_ERROR;
-                  }
-              }
-
-            db->tree_conflict_reason = CONFLICT_REASON_SKIP;
-            db->skip_reason = obstr_state;
-
-            if (! right_source)
-              {
-                *skip = *skip_children = TRUE;
-                SVN_ERR(mark_dir_edited(merge_b, db, local_abspath,
-                                        scratch_pool));
-              }
-
-            return SVN_NO_ERROR;
-          }
-
-        if (is_deleted)
-          db->tree_conflict_local_node_kind = svn_node_none;
-      }
-
-      if (db->tree_conflict_local_node_kind == svn_node_none)
-        {
-          db->shadowed = TRUE;
-
-          /* If this is not the merge target and the parent is too shallow to
-             contain this directory, and the directory is not presen
-             via exclusion or depth filtering, skip it instead of recording
-             a tree conflict.
-
-             Non-inheritable mergeinfo will be recorded, allowing
-             future merges into non-shallow working copies to merge
-             changes we missed this time around. */
-          if (pdb && (excluded
-                      || (parent_depth != svn_depth_unknown &&
-                          parent_depth < svn_depth_immediates)))
-            {
-              db->shadowed = TRUE;
-
-              db->tree_conflict_reason = CONFLICT_REASON_SKIP;
-              db->skip_reason = svn_wc_notify_state_missing;
-
-              return SVN_NO_ERROR;
-            }
-
-          if (is_deleted)
-            db->tree_conflict_reason = svn_wc_conflict_reason_deleted;
-          else
-            db->tree_conflict_reason = svn_wc_conflict_reason_missing;
-
-          /* ### To avoid breaking tests */
-          *skip = TRUE;
-          *skip_children = TRUE;
-          SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
-          return SVN_NO_ERROR;
-          /* ### /avoid breaking tests */
-        }
-      else if (db->tree_conflict_local_node_kind != svn_node_dir)
-        {
-          svn_boolean_t added;
-
-          db->shadowed = TRUE;
-          SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
-                                        local_abspath, scratch_pool));
-
-          db->tree_conflict_reason = added ? svn_wc_conflict_reason_added
-                                           : svn_wc_conflict_reason_obstructed;
-
-          /* ### To avoid breaking tests */
-          *skip = TRUE;
-          *skip_children = TRUE;
-          SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
-          return SVN_NO_ERROR;
-          /* ### /avoid breaking tests */
-        }
-
-      if (! right_source)
-        {
-          /* We want to delete the directory */
-          /* Mark PB edited now? */
-          db->tree_conflict_action = svn_wc_conflict_action_delete;
-          SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
-
-          if (db->shadowed)
-            {
-              *skip_children = TRUE;
-              return SVN_NO_ERROR; /* Already set a tree conflict */
-            }
-
-          db->delete_state = (pdb != NULL) ? pdb->delete_state : NULL;
-
-          if (db->delete_state && db->delete_state->found_edit)
-            {
-              /* A sibling found a conflict. Done. */
-              *skip = TRUE;
-              *skip_children = TRUE;
-            }
-          else if (merge_b->force_delete)
-            {
-              /* No comparison necessary */
-              *skip_children = TRUE;
-            }
-          else if (! db->delete_state)
-            {
-              /* Start descendant comparison */
-              db->delete_state = apr_pcalloc(db->pool,
-                                             sizeof(*db->delete_state));
-
-              db->delete_state->del_root = db;
-              db->delete_state->compared_abspaths = apr_hash_make(db->pool);
-            }
-        }
-    }
-  else
-    {
-      const svn_wc_conflict_description2_t *old_tc = NULL;
-
-      /* The node doesn't exist pre-merge: We have an addition */
-      db->added = TRUE;
-      db->tree_conflict_action = svn_wc_conflict_action_add;
-
-      if (pdb && pdb->pending_deletes
-          && svn_hash_gets(pdb->pending_deletes, local_abspath))
-        {
-          db->add_is_replace = TRUE;
-          db->tree_conflict_action = svn_wc_conflict_action_replace;
-
-          svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
-        }
-
-      if (pdb
-          && pdb->new_tree_conflicts
-          && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
-        {
-          db->tree_conflict_action = svn_wc_conflict_action_replace;
-          db->tree_conflict_reason = old_tc->reason;
-
-          if (old_tc->reason == svn_wc_conflict_reason_deleted
-             || old_tc->reason == svn_wc_conflict_reason_moved_away)
-            {
-              /* Issue #3806: Incoming replacements on local deletes produce
-                 inconsistent result.
-
-                 In this specific case we can continue applying the add part
-                 of the replacement. */
-            }
-          else
-            {
-              *skip = TRUE;
-              *skip_children = TRUE;
-
-              /* Update the tree conflict to store that this is a replace */
-              SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
-                                           old_tc->node_kind,

[... 754 lines stripped ...]


Reply via email to