Author: rinrab
Date: Sun Nov 24 23:32:26 2024
New Revision: 1922065

URL: http://svn.apache.org/viewvc?rev=1922065&view=rev
Log:
On the 'apply-processor' branch: (going to follow up r1922048)

Move filter_self_referential_mergeinfo() function back to the merge.c
file from merge_processor.c. Also move the split_mergeinfo_on_revision()
function there as a dependency.

I just noticed that we'll need it there.

Note: these functions are not being referenced from anywhere, since all the
usages are currently disabled using #if TODO_FILTER_MERGEINFO, so there are
no functional changes, only the move of unreferenced function.

* subversion/libsvn_client/merge_processor.c
  (split_mergeinfo_on_revision,
   filter_self_referential_mergeinfo): moved from...

* subversion/libsvn_client/merge.c
  (split_mergeinfo_on_revision,
   filter_self_referential_mergeinfo): ...moved to

Modified:
    subversion/branches/apply-processor/subversion/libsvn_client/merge.c
    
subversion/branches/apply-processor/subversion/libsvn_client/merge_processor.c

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=1922065&r1=1922064&r2=1922065&view=diff
==============================================================================
--- subversion/branches/apply-processor/subversion/libsvn_client/merge.c 
(original)
+++ subversion/branches/apply-processor/subversion/libsvn_client/merge.c Sun 
Nov 24 23:32:26 2024
@@ -604,6 +604,380 @@ find_nearest_ancestor(const apr_array_he
   return NULL;
 }
 
+
+/* 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;
+}
+
+
+/* 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;
+}
+
 /* Find the highest level path in a merge target (possibly the merge target
    itself) to use in a merge notification header.
 

Modified: 
subversion/branches/apply-processor/subversion/libsvn_client/merge_processor.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/apply-processor/subversion/libsvn_client/merge_processor.c?rev=1922065&r1=1922064&r2=1922065&view=diff
==============================================================================
--- 
subversion/branches/apply-processor/subversion/libsvn_client/merge_processor.c 
(original)
+++ 
subversion/branches/apply-processor/subversion/libsvn_client/merge_processor.c 
Sun Nov 24 23:32:26 2024
@@ -233,405 +233,6 @@ make_conflict_versions(const svn_wc_conf
   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.
 



Reply via email to