You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2022/01/14 14:01:51 UTC

svn commit: r1897034 [17/37] - in /subversion/branches/multi-wc-format: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ contrib/client-side/ contrib/client-side/svn_load_dirs/ contrib/hook-scripts/ contrib/s...

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/merge.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/merge.c Fri Jan 14 14:01:45 2022
@@ -51,6 +51,7 @@
 #include "svn_sorts.h"
 #include "svn_subst.h"
 #include "svn_ra.h"
+#include "svn_version.h"
 #include "client.h"
 #include "mergeinfo.h"
 
@@ -264,7 +265,7 @@ typedef struct merge_cmd_baton_t {
 
   /* Reference to the one-and-only CHILDREN_WITH_MERGEINFO (see global
      comment) or a similar list for single-file-merges */
-  const apr_array_header_t *children_with_mergeinfo;
+  apr_array_header_t *children_with_mergeinfo;
 
   svn_client_ctx_t *ctx;              /* Client context for callbacks, etc. */
 
@@ -422,6 +423,37 @@ merge_source_dup(const merge_source_t *s
   return s;
 }
 
+/* Decide whether ambiguous foreign merge should be a warning or an error */
+#define WITH_AMBIGUOUS_FOREIGN_MERGE_WARNING \
+          (SVN_VER_MAJOR == 1 && SVN_VER_MINOR < 16)
+
+#if WITH_AMBIGUOUS_FOREIGN_MERGE_WARNING
+/* Notify a warning, given in WARNING, if WARNING is non-null.
+ *
+ * We plan to replace this with a hard error in Subversion 1.16.
+ * This clears the error object WARNING before returning.
+ */
+static void
+notify_pre_1_16_warning(svn_error_t *warning,
+                        svn_client_ctx_t *ctx,
+                        apr_pool_t *pool)
+{
+  if (!warning)
+    return;
+
+  if (ctx->notify_func2)
+    {
+      svn_wc_notify_t *n
+        = svn_wc_create_notify("" /*path*/, svn_wc_notify_warning, pool);
+
+      n->err = svn_error_quick_wrap(warning,
+                 _("In Subversion 1.16 this warning will become a fatal error"));
+      ctx->notify_func2(ctx->notify_baton2, n, pool);
+    }
+  svn_error_clear(warning);
+}
+#endif
+
 /* Return SVN_ERR_UNSUPPORTED_FEATURE if URL is not inside the repository
    of LOCAL_ABSPATH.  Use SCRATCH_POOL for temporary allocations. */
 static svn_error_t *
@@ -440,38 +472,70 @@ check_repos_match(const merge_target_t *
   return SVN_NO_ERROR;
 }
 
-/* Return TRUE iff the repository of LOCATION1 is the same as
- * that of LOCATION2.  If STRICT_URLS is true, the URLs must
- * match (and the UUIDs, just to be sure), otherwise just the UUIDs must
- * match and the URLs can differ (a common case is http versus https). */
-static svn_boolean_t
-is_same_repos(const svn_client__pathrev_t *location1,
+/* Decide whether LOCATION1 and LOCATION2 point to the same repository
+ * (with the same root URL) or to two different repositories.
+ *   - same repository root URL         -> set *SAME_REPOS true
+ *   - different repositories           -> set *SAME_REPOS false
+ *   - different URLs but same UUID     -> return an error
+ *
+ * The last case is unsupported for practical and historical reasons
+ * (see issue #4874) even though different URLs pointing to the same or
+ * equivalent repositories could be supported in principle.
+ */
+static svn_error_t *
+is_same_repos(svn_boolean_t *same_repos,
+              const svn_client__pathrev_t *location1,
+              const char *path1,
               const svn_client__pathrev_t *location2,
-              svn_boolean_t strict_urls)
+              const char *path2,
+              const char *message)
 {
-  if (strict_urls)
-    return (strcmp(location1->repos_root_url, location2->repos_root_url) == 0
-            && strcmp(location1->repos_uuid, location2->repos_uuid) == 0);
+  if (strcmp(location1->repos_root_url, location2->repos_root_url) == 0)
+    *same_repos = TRUE;
+  else if (strcmp(location1->repos_uuid, location2->repos_uuid) != 0)
+    *same_repos = FALSE;
   else
-    return (strcmp(location1->repos_uuid, location2->repos_uuid) == 0);
+    {
+      svn_error_t *err
+        = svn_error_create(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, message);
+
+      return svn_error_quick_wrapf(err,
+               _("The locations '%s' and '%s' point to repositories with the "
+                 "same repository UUID using different repository root URLs "
+                 "('%s' and '%s')"),
+               path1, path2,
+               location1->repos_root_url, location2->repos_root_url);
+    }
+  return SVN_NO_ERROR;
 }
 
-/* If the repository identified of LOCATION1 is not the same as that
- * of LOCATION2, throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES
- * error mentioning PATH1 and PATH2. For STRICT_URLS, see is_same_repos().
+/* Check that LOCATION1 and LOCATION2 point to the same repository, with
+ * the same root URL.  If not, throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES
+ * error mentioning PATH_OR_URL1 and PATH_OR_URL2.
  */
 static svn_error_t *
 check_same_repos(const svn_client__pathrev_t *location1,
-                 const char *path1,
+                 const char *path_or_url1,
                  const svn_client__pathrev_t *location2,
-                 const char *path2,
-                 svn_boolean_t strict_urls,
-                 apr_pool_t *scratch_pool)
+                 const char *path_or_url2,
+                 const char *message)
 {
-  if (! is_same_repos(location1, location2, strict_urls))
-    return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
-                             _("'%s' must be from the same repository as "
-                               "'%s'"), path1, path2);
+  svn_boolean_t same_repos;
+
+  SVN_ERR(is_same_repos(&same_repos,
+                        location1, path_or_url1, location2, path_or_url2,
+                        message));
+  if (! same_repos)
+    {
+      svn_error_t *err
+        = svn_error_create(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, message);
+
+      return svn_error_quick_wrapf(err,
+               _("The locations '%s' and '%s' point to different repositories "
+                 "(root URLs '%s' and '%s', and differing UUIDs)"),
+               path_or_url1, path_or_url2,
+               location1->repos_root_url, location2->repos_root_url);
+    }
   return SVN_NO_ERROR;
 }
 
@@ -1545,6 +1609,25 @@ record_update_delete(merge_cmd_baton_t *
                     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;
 }
 
@@ -3387,7 +3470,7 @@ merge_dir_closed(const char *relpath,
 
    We register a skipped path, which will make parent mergeinfo non-
    inheritable. This ensures that a future merge might see these skipped
-   changes as eligable for merging.
+   changes as eligible for merging.
 
    For legacy reasons we also notify the path as skipped.
  */
@@ -5595,9 +5678,9 @@ svn_client__make_merge_conflict_error(sv
    with paths (svn_client__merge_path_t *) arranged in depth first order,
    which have mergeinfo set on them or meet one of the other criteria
    defined in get_mergeinfo_paths().  Remove any paths absent from disk
-   or scheduled for deletion from CHILDREN_WITH_MERGEINFO which are equal to
+   from CHILDREN_WITH_MERGEINFO which are equal to
    or are descendants of TARGET_WCPATH by setting those children to NULL. */
-static void
+static svn_error_t *
 remove_absent_children(const char *target_wcpath,
                        apr_array_header_t *children_with_mergeinfo)
 {
@@ -5609,12 +5692,13 @@ remove_absent_children(const char *targe
     {
       svn_client__merge_path_t *child =
         APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
-      if ((child->absent || child->scheduled_for_deletion)
+      if (child->absent
           && svn_dirent_is_ancestor(target_wcpath, child->abspath))
         {
-          svn_sort__array_delete(children_with_mergeinfo, i--, 1);
+          SVN_ERR(svn_sort__array_delete2(children_with_mergeinfo, i--, 1));
         }
     }
+  return SVN_NO_ERROR;
 }
 
 /* Helper for do_directory_merge() to handle the case where a merge editor
@@ -5629,14 +5713,14 @@ remove_absent_children(const char *targe
    MERGE_B->target->abspath, this must always be present in
    CHILDREN_WITH_MERGEINFO so this is never removed by this
    function. */
-static void
+static svn_error_t *
 remove_children_with_deleted_mergeinfo(merge_cmd_baton_t *merge_b,
                                        apr_array_header_t *children_with_mergeinfo)
 {
   int i;
 
   if (!merge_b->paths_with_deleted_mergeinfo)
-    return;
+    return SVN_NO_ERROR;
 
   /* CHILDREN_WITH_MERGEINFO[0] is the always the merge target
      so start at the first child. */
@@ -5647,9 +5731,10 @@ remove_children_with_deleted_mergeinfo(m
 
       if (svn_hash_gets(merge_b->paths_with_deleted_mergeinfo, child->abspath))
         {
-          svn_sort__array_delete(children_with_mergeinfo, i--, 1);
+          SVN_ERR(svn_sort__array_delete2(children_with_mergeinfo, i--, 1));
         }
     }
+  return SVN_NO_ERROR;
 }
 
 /* Helper for do_directory_merge().
@@ -5975,7 +6060,7 @@ get_most_inclusive_rev(const apr_array_h
    remaining_ranges is inclusive of END_REV, Slice the first range in
    to two at END_REV. All the allocations are persistent and allocated
    from POOL. */
-static void
+static svn_error_t *
 slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
                        svn_boolean_t is_rollback, svn_revnum_t end_rev,
                        apr_pool_t *pool)
@@ -6005,10 +6090,12 @@ slice_remaining_ranges(apr_array_header_
               split_range2->start = end_rev;
               APR_ARRAY_IDX(child->remaining_ranges, 0,
                             svn_merge_range_t *) = split_range1;
-              svn_sort__array_insert(child->remaining_ranges, &split_range2, 1);
+              SVN_ERR(svn_sort__array_insert2(child->remaining_ranges,
+                                              &split_range2, 1));
             }
         }
     }
+  return SVN_NO_ERROR;
 }
 
 /* Helper for do_directory_merge().
@@ -6020,7 +6107,7 @@ slice_remaining_ranges(apr_array_header_
    If a range is removed from a child's remaining_ranges array, allocate the
    new remaining_ranges array in POOL.
  */
-static void
+static svn_error_t *
 remove_first_range_from_remaining_ranges(svn_revnum_t revision,
                                          apr_array_header_t
                                            *children_with_mergeinfo,
@@ -6041,10 +6128,11 @@ remove_first_range_from_remaining_ranges
             APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
           if (first_range->end == revision)
             {
-              svn_sort__array_delete(child->remaining_ranges, 0, 1);
+              SVN_ERR(svn_sort__array_delete2(child->remaining_ranges, 0, 1));
             }
         }
     }
+  return SVN_NO_ERROR;
 }
 
 /* Get a file's content and properties from the repository.
@@ -6130,7 +6218,7 @@ get_child_with_mergeinfo(const apr_array
        out of order and then sort afterwards. (One caller is doing a qsort
        after calling this anyway.)
  */
-static void
+static svn_error_t *
 insert_child_to_merge(apr_array_header_t *children_with_mergeinfo,
                       const svn_client__merge_path_t *insert_element,
                       apr_pool_t *pool)
@@ -6144,7 +6232,9 @@ insert_child_to_merge(apr_array_header_t
                                   compare_merge_path_t_as_paths);
 
   new_element = svn_client__merge_path_dup(insert_element, pool);
-  svn_sort__array_insert(children_with_mergeinfo, &new_element, insert_index);
+  SVN_ERR(svn_sort__array_insert2(children_with_mergeinfo,
+                                  &new_element, insert_index));
+  return SVN_NO_ERROR;
 }
 
 /* Helper for get_mergeinfo_paths().
@@ -6205,7 +6295,7 @@ insert_parent_and_sibs_of_sw_absent_del_
       parent->missing_child = child->absent;
       parent->switched_child = child->switched;
       /* Insert PARENT into CHILDREN_WITH_MERGEINFO. */
-      insert_child_to_merge(children_with_mergeinfo, parent, pool);
+      SVN_ERR(insert_child_to_merge(children_with_mergeinfo, parent, pool));
       /* Increment for loop index so we don't process the inserted element. */
       (*curr_index)++;
     } /*(parent == NULL) */
@@ -6242,8 +6332,8 @@ insert_parent_and_sibs_of_sw_absent_del_
 
           sibling_of_missing = svn_client__merge_path_create(child_abspath,
                                                              pool);
-          insert_child_to_merge(children_with_mergeinfo, sibling_of_missing,
-                                pool);
+          SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
+                                        sibling_of_missing, pool));
         }
     }
 
@@ -6584,8 +6674,8 @@ get_mergeinfo_paths(apr_array_header_t *
                svn_client__merge_path_t *switched_child =
                  svn_client__merge_path_create(wc_path, result_pool);
                switched_child->switched = TRUE;
-               insert_child_to_merge(children_with_mergeinfo, switched_child,
-                                     result_pool);
+               SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
+                                             switched_child, result_pool));
              }
         }
     }
@@ -6637,8 +6727,8 @@ get_mergeinfo_paths(apr_array_header_t *
             }
 
           if (new_shallow_child)
-            insert_child_to_merge(children_with_mergeinfo, shallow_child,
-                                  result_pool);
+            SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
+                                          shallow_child, result_pool));
        }
     }
 
@@ -6667,8 +6757,8 @@ get_mergeinfo_paths(apr_array_header_t *
                svn_client__merge_path_t *absent_child =
                  svn_client__merge_path_create(wc_path, result_pool);
                absent_child->absent = TRUE;
-               insert_child_to_merge(children_with_mergeinfo, absent_child,
-                                     result_pool);
+               SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
+                                             absent_child, result_pool));
              }
         }
     }
@@ -6681,8 +6771,8 @@ get_mergeinfo_paths(apr_array_header_t *
       svn_client__merge_path_t *target_child =
         svn_client__merge_path_create(target->abspath,
                                       result_pool);
-      insert_child_to_merge(children_with_mergeinfo, target_child,
-                            result_pool);
+      SVN_ERR(insert_child_to_merge(children_with_mergeinfo, target_child,
+                                    result_pool));
     }
 
   /* Case 8: Path is an immediate *directory* child of
@@ -6725,8 +6815,8 @@ get_mergeinfo_paths(apr_array_header_t *
                       && depth == svn_depth_immediates)
                     immediate_child->immediate_child_dir = TRUE;
 
-                  insert_child_to_merge(children_with_mergeinfo,
-                                        immediate_child, result_pool);
+                  SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
+                                                immediate_child, result_pool));
                 }
             }
         }
@@ -6818,9 +6908,9 @@ get_mergeinfo_paths(apr_array_header_t *
                   child_of_noninheritable =
                     svn_client__merge_path_create(child_abspath, result_pool);
                   child_of_noninheritable->child_of_noninheritable = TRUE;
-                  insert_child_to_merge(children_with_mergeinfo,
-                                        child_of_noninheritable,
-                                        result_pool);
+                  SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
+                                                child_of_noninheritable,
+                                                result_pool));
                   if (!dry_run && same_repos)
                     {
                       svn_mergeinfo_t mergeinfo;
@@ -7244,7 +7334,7 @@ normalize_merge_sources_internal(apr_arr
                   new_segment->path = original_repos_relpath;
                   new_segment->range_start = original_revision;
                   new_segment->range_end = original_revision;
-                  svn_sort__array_insert(segments, &new_segment, 0);
+                  SVN_ERR(svn_sort__array_insert2(segments, &new_segment, 0));
                 }
             }
         }
@@ -7791,7 +7881,7 @@ do_file_merge(svn_mergeinfo_catalog_t re
              (This list is used from notify_merge_begin)
 
             Directory merges use remove_first_range_from_remaining_ranges() */
-          svn_sort__array_delete(ranges_to_merge, 0, 1);
+          SVN_ERR(svn_sort__array_delete2(ranges_to_merge, 0, 1));
         }
       merge_b->notify_begin.last_abspath = NULL;
     } /* !merge_b->record_only */
@@ -7889,18 +7979,23 @@ process_children_with_new_mergeinfo(merg
                                     apr_pool_t *pool)
 {
   apr_pool_t *iterpool;
-  apr_hash_index_t *hi;
+  apr_array_header_t *a;
+  int i;
 
   if (!merge_b->paths_with_new_mergeinfo || merge_b->dry_run)
     return SVN_NO_ERROR;
 
-  /* Iterate over each path with explicit mergeinfo added by the merge. */
+  /* Iterate over each path with explicit mergeinfo added by the merge.
+   * Iterate in a parent-to-child order so that inherited
+   * mergeinfo is propagated consistently from each parent path to its
+   * children. (Issue #4862) */
+  a = svn_sort__hash(merge_b->paths_with_new_mergeinfo,
+                     svn_sort_compare_items_as_paths, pool);
   iterpool = svn_pool_create(pool);
-  for (hi = apr_hash_first(pool, merge_b->paths_with_new_mergeinfo);
-       hi;
-       hi = apr_hash_next(hi))
+  for (i = 0; i < a->nelts; i++)
     {
-      const char *abspath_with_new_mergeinfo = apr_hash_this_key(hi);
+      svn_sort__item_t *item = &APR_ARRAY_IDX(a, i, svn_sort__item_t);
+      const char *abspath_with_new_mergeinfo = item->key;
       svn_mergeinfo_t path_inherited_mergeinfo;
       svn_mergeinfo_t path_explicit_mergeinfo;
       svn_client__merge_path_t *new_child;
@@ -7978,7 +8073,8 @@ process_children_with_new_mergeinfo(merg
               /* Set the path's remaining_ranges equal to its parent's. */
               new_child->remaining_ranges = svn_rangelist_dup(
                  parent->remaining_ranges, pool);
-              insert_child_to_merge(children_with_mergeinfo, new_child, pool);
+              SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
+                                            new_child, pool));
             }
         }
     }
@@ -8543,8 +8639,8 @@ record_mergeinfo_for_dir_merge(svn_merge
   /* Remove absent children at or under MERGE_B->target->abspath from
      CHILDREN_WITH_MERGEINFO
      before we calculate the merges performed. */
-  remove_absent_children(merge_b->target->abspath,
-                         children_with_mergeinfo);
+  SVN_ERR(remove_absent_children(merge_b->target->abspath,
+                                 children_with_mergeinfo));
 
   /* Determine which subtrees of interest need mergeinfo recorded... */
   SVN_ERR(flag_subtrees_needing_mergeinfo(operative_merge, &range,
@@ -9510,8 +9606,9 @@ do_mergeinfo_aware_dir_merge(svn_mergein
 
               svn_pool_clear(iterpool);
 
-              slice_remaining_ranges(children_with_mergeinfo,
-                                     is_rollback, end_rev, scratch_pool);
+              SVN_ERR(slice_remaining_ranges(children_with_mergeinfo,
+                                             is_rollback, end_rev,
+                                             scratch_pool));
 
               /* Reset variables that must be reset for every drive */
               merge_b->notify_begin.last_abspath = NULL;
@@ -9540,12 +9637,12 @@ do_mergeinfo_aware_dir_merge(svn_mergein
                  to consider these subtrees for subsequent editor drives
                  nor do we want to record mergeinfo on them describing
                  the merge itself. */
-              remove_children_with_deleted_mergeinfo(
-                merge_b, children_with_mergeinfo);
+              SVN_ERR(remove_children_with_deleted_mergeinfo(
+                        merge_b, children_with_mergeinfo));
 
               /* Prepare for the next iteration (if any). */
-              remove_first_range_from_remaining_ranges(
-                end_rev, children_with_mergeinfo, scratch_pool);
+              SVN_ERR(remove_first_range_from_remaining_ranges(
+                        end_rev, children_with_mergeinfo, scratch_pool));
 
               /* If we raised any conflicts, break out and report how much
                  we have merged. */
@@ -10498,16 +10595,34 @@ svn_client__merge_locked(svn_client__con
             source2, NULL, revision2, revision2, ctx, sesspool));
 
   /* We can't do a diff between different repositories. */
-  /* ### We should also insist that the root URLs of the two sources match,
-   *     as we are only carrying around a single source-repos-root from now
-   *     on, and URL calculations will go wrong if they differ.
-   *     Alternatively, teach the code to cope with differing root URLs. */
-  SVN_ERR(check_same_repos(source1_loc, source1_loc->url,
-                           source2_loc, source2_loc->url,
-                           FALSE /* strict_urls */, scratch_pool));
+  err = check_same_repos(
+          source1_loc, source1, source2_loc, source2,
+          _("The repository root URLs of the two sources must be identical "
+            "in a two-URL merge"));
+  if (err)
+    /* Add an error code and message compatible with Subversion <= 1.14 */
+    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, err,
+             _("'%s' isn't in the same repository as '%s'"),
+             source1, source2_loc->repos_root_url);
 
   /* Do our working copy and sources come from the same repository? */
-  same_repos = is_same_repos(&target->loc, source1_loc, TRUE /* strict_urls */);
+  err = is_same_repos(&same_repos,
+          source1_loc, source1, &target->loc, target_abspath,
+          _("The given merge source must have the same repository "
+            "root URL as the target WC, in the usual case; "
+            "or, if a foreign repository merge is intended, the repositories "
+            "must have different root URLs and different UUIDs"));
+#if WITH_AMBIGUOUS_FOREIGN_MERGE_WARNING
+  if (err)
+    {
+      /* Subversion 1.14 compatibility: do a foreign-repository merge,
+       * even though the repository UUIDs are identical. Issue a warning. */
+      notify_pre_1_16_warning(err, ctx, scratch_pool);
+      same_repos = FALSE;
+    }
+#else
+  SVN_ERR(err);
+#endif
 
   /* Unless we're ignoring ancestry, see if the two sources are related.  */
   if (! ignore_mergeinfo)
@@ -11713,13 +11828,12 @@ open_reintegrate_source_and_target(svn_r
 
   /* source_loc and target->loc are required to be in the same repository,
      as mergeinfo doesn't come into play for cross-repository merging. */
-  SVN_ERR(check_same_repos(source_loc,
-                           svn_dirent_local_style(source_path_or_url,
-                                                  scratch_pool),
-                           &target->loc,
-                           svn_dirent_local_style(target->abspath,
-                                                  scratch_pool),
-                           TRUE /* strict_urls */, scratch_pool));
+  SVN_ERR(check_same_repos(
+            source_loc, source_path_or_url,
+            &target->loc, svn_dirent_local_style(target->abspath,
+                                                 scratch_pool),
+            _("The repository root URLs of the source and the target WC "
+              "must be identical in a reintegrate merge")));
 
   *source_loc_p = source_loc;
   *target_p = target;
@@ -11881,7 +11995,24 @@ merge_peg_locked(svn_client__conflict_re
                                   scratch_pool, scratch_pool));
 
   /* Check for same_repos. */
-  same_repos = is_same_repos(&target->loc, source_loc, TRUE /* strict_urls */);
+  err = is_same_repos(&same_repos,
+            source_loc, source_path_or_url,
+            &target->loc, target_abspath,
+            _("The given merge source must have the same repository "
+              "root URL as the target WC, in the usual case; "
+              "or, if a foreign repository merge is intended, the repositories "
+              "must have different root URLs and different UUIDs"));
+#if WITH_AMBIGUOUS_FOREIGN_MERGE_WARNING
+  if (err)
+    {
+      /* Subversion 1.14 compatibility: do a foreign-repository merge,
+       * even though the repository UUIDs are identical. Issue a warning. */
+      notify_pre_1_16_warning(err, ctx, scratch_pool);
+      same_repos = FALSE;
+    }
+#else
+  SVN_ERR(err);
+#endif
 
   /* Do the real merge!  (We say with confidence that our merge
      sources are both ancestral and related.) */
@@ -12669,9 +12800,12 @@ client_find_automatic_merge(automatic_me
             ctx, result_pool));
 
   /* Check source is in same repos as target. */
-  SVN_ERR(check_same_repos(s_t->source, source_path_or_url,
-                           &s_t->target->loc, target_abspath,
-                           TRUE /* strict_urls */, scratch_pool));
+  SVN_ERR(check_same_repos(
+            s_t->source, source_path_or_url,
+            &s_t->target->loc, svn_dirent_local_style(target_abspath,
+                                                      scratch_pool),
+            _("The repository root URLs of the source and the target WC "
+              "must be identical in an automatic merge")));
 
   SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
                                ctx, result_pool, scratch_pool));
@@ -12814,7 +12948,7 @@ do_automatic_merge_locked(svn_client__co
          for the root path of the merge).
 
          An improvement would be to change find_automatic_merge() to
-         find the base for each sutree, and then here use the oldest base
+         find the base for each subtree, and then here use the oldest base
          among all subtrees. */
       apr_array_header_t *merge_sources;
       svn_ra_session_t *ra_session = NULL;

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/mergeinfo.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/mergeinfo.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/mergeinfo.c Fri Jan 14 14:01:45 2022
@@ -1749,7 +1749,7 @@ svn_client__mergeinfo_log(svn_boolean_t
       if (*target_mergeinfo_catalog)
         {
           /* The caller provided the mergeinfo catalog for
-             TARGET_PATH_OR_URL, so we don't need to accquire
+             TARGET_PATH_OR_URL, so we don't need to acquire
              it ourselves.  We do need to get the repos_root
              though, because get_mergeinfo() won't do it for us. */
           target_mergeinfo_cat = *target_mergeinfo_catalog;

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/mergeinfo.h
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/mergeinfo.h?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/mergeinfo.h (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/mergeinfo.h Fri Jan 14 14:01:45 2022
@@ -74,8 +74,6 @@ typedef struct svn_client__merge_path_t
                                            prior to a merge.  May be NULL. */
   svn_boolean_t inherited_mergeinfo;    /* Whether PRE_MERGE_MERGEINFO was
                                            explicit or inherited. */
-  svn_boolean_t scheduled_for_deletion; /* ABSPATH is scheduled for
-                                           deletion. */
   svn_boolean_t immediate_child_dir;    /* ABSPATH is an immediate child
                                            directory of the merge target,
                                            has no explicit mergeinfo prior
@@ -316,7 +314,7 @@ svn_client__get_history_as_mergeinfo(svn
 
 /* Parse any explicit mergeinfo on LOCAL_ABSPATH and store it in
    *MERGEINFO.  If no record of any mergeinfo exists, set *MERGEINFO to NULL.
-   Does not acount for inherited mergeinfo.
+   Does not account for inherited mergeinfo.
 
    Allocate the result deeply in @a result_pool. */
 svn_error_t *

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/mtcc.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/mtcc.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/mtcc.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/mtcc.c Fri Jan 14 14:01:45 2022
@@ -453,7 +453,8 @@ mtcc_verify_create(svn_client__mtcc_t *m
 
       if (op)
         return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
-                                 _("Path '%s' already exists"),
+                                 _("Path '%s' already exists, or was created "
+                                   "by an earlier operation"),
                                  new_relpath);
 
       SVN_ERR(mtcc_op_find(&op, NULL, new_relpath, mtcc->root_op, TRUE, TRUE,
@@ -604,7 +605,7 @@ mtcc_op_contains_non_delete(const mtcc_o
 static svn_error_t *
 mtcc_add_delete(const char *relpath,
                 svn_boolean_t for_move,
-                svn_client__mtcc_t *mtcc,                
+                svn_client__mtcc_t *mtcc,
                 apr_pool_t *scratch_pool)
 {
   mtcc_op_t *op;
@@ -636,7 +637,7 @@ mtcc_add_delete(const char *relpath,
         {
           /* Allow deleting directories, that are unmodified except for
               one or more deleted descendants */
-          
+
           SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, TRUE,
                   FALSE, FALSE, mtcc->pool, scratch_pool));
 

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/patch.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/patch.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/patch.c Fri Jan 14 14:01:45 2022
@@ -1020,6 +1020,7 @@ init_patch_target(patch_target_t **patch
   target_content_t *content;
   svn_boolean_t has_text_changes = FALSE;
   svn_boolean_t follow_moves;
+  const char *tempdir_abspath;
 
   has_text_changes = ((patch->hunks && patch->hunks->nelts > 0)
                       || patch->binary_patch);
@@ -1225,8 +1226,10 @@ init_patch_target(patch_target_t **patch
         }
 
       /* Open a temporary file to write the patched result to. */
+      SVN_ERR(svn_wc__get_tmpdir(&tempdir_abspath, wc_ctx,
+          target->local_abspath, scratch_pool, scratch_pool));
       SVN_ERR(svn_io_open_unique_file3(&target->patched_file,
-                                       &target->patched_path, NULL,
+                                       &target->patched_path, tempdir_abspath,
                                        remove_tempfiles ?
                                          svn_io_file_del_on_pool_cleanup :
                                          svn_io_file_del_none,
@@ -1238,7 +1241,7 @@ init_patch_target(patch_target_t **patch
 
       /* Open a temporary stream to write rejected hunks to. */
       SVN_ERR(svn_stream_open_unique(&target->reject_stream,
-                                     &target->reject_path, NULL,
+                                     &target->reject_path, tempdir_abspath,
                                      remove_tempfiles ?
                                          svn_io_file_del_on_pool_cleanup :
                                          svn_io_file_del_none,
@@ -2467,7 +2470,7 @@ send_patch_notification(const patch_targ
 
 /* Implements the callback for svn_sort__array.  Puts hunks that match
    before hunks that do not match, puts hunks that match in order
-   based on postion matched, puts hunks that do not match in order
+   based on position matched, puts hunks that do not match in order
    based on original position. */
 static int
 sort_matched_hunks(const void *a, const void *b)

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/ra.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/ra.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/ra.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/ra.c Fri Jan 14 14:01:45 2022
@@ -402,8 +402,7 @@ svn_client__open_ra_session_internal(svn
         }
     }
 
-  /* If the caller allows for auto-following redirections, and the
-     RA->open() call above reveals a CORRECTED_URL, try the new URL.
+  /* If the caller allows for auto-following redirections, try the new URL.
      We'll do this in a loop up to some maximum number follow-and-retry
      attempts.  */
   if (corrected_url)
@@ -414,12 +413,14 @@ svn_client__open_ra_session_internal(svn
       *corrected_url = NULL;
       while (attempts_left--)
         {
-          const char *corrected = NULL;
+          const char *corrected = NULL; /* canonicalized version */
+          const char *redirect_url = NULL; /* non-canonicalized version */
 
           /* Try to open the RA session.  If this is our last attempt,
              don't accept corrected URLs from the RA provider. */
-          SVN_ERR(svn_ra_open4(ra_session,
+          SVN_ERR(svn_ra_open5(ra_session,
                                attempts_left == 0 ? NULL : &corrected,
+                               attempts_left == 0 ? NULL : &redirect_url,
                                base_url, uuid, cbtable, cb, ctx->config,
                                result_pool));
 
@@ -441,19 +442,28 @@ svn_client__open_ra_session_internal(svn
           *corrected_url = corrected;
 
           /* Make sure we've not attempted this URL before. */
-          if (svn_hash_gets(attempted, corrected))
+          if (svn_hash_gets(attempted, redirect_url))
             return svn_error_createf(SVN_ERR_CLIENT_CYCLE_DETECTED, NULL,
                                      _("Redirect cycle detected for URL '%s'"),
-                                     corrected);
+                                     redirect_url);
+
+          /*
+           * Remember this redirect URL so we don't wind up in a loop.
+           *
+           * Store the non-canonicalized version of the URL. The canonicalized
+           * version is insufficient for loop detection because we might not get
+           * an exact match against URLs used by the RA protocol-layer (the URL
+           * used by the protocol may contain trailing slashes, for example,
+           * which are stripped during canonicalization).
+           */
+          svn_hash_sets(attempted, redirect_url, (void *)1);
 
-          /* Remember this CORRECTED_URL so we don't wind up in a loop. */
-          svn_hash_sets(attempted, corrected, (void *)1);
           base_url = corrected;
         }
     }
   else
     {
-      SVN_ERR(svn_ra_open4(ra_session, NULL, base_url,
+      SVN_ERR(svn_ra_open5(ra_session, NULL, NULL, base_url,
                            uuid, cbtable, cb, ctx->config, result_pool));
     }
 

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/repos_diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/repos_diff.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/repos_diff.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/repos_diff.c Fri Jan 14 14:01:45 2022
@@ -384,7 +384,7 @@ get_file_from_ra(struct file_baton *fb,
      See https://issues.apache.org/jira/browse/SVN-3657#desc9 and
      http://svn.haxx.se/dev/archive-2010-08/0351.shtml for more details.
  */
-static void
+static svn_error_t *
 remove_non_prop_changes(apr_hash_t *pristine_props,
                         apr_array_header_t *changes)
 {
@@ -392,7 +392,7 @@ remove_non_prop_changes(apr_hash_t *pris
 
   /* For added nodes, there is nothing to filter. */
   if (apr_hash_count(pristine_props) == 0)
-    return;
+    return SVN_NO_ERROR;
 
   for (i = 0; i < changes->nelts; i++)
     {
@@ -406,11 +406,12 @@ remove_non_prop_changes(apr_hash_t *pris
           if (old_val && svn_string_compare(old_val, change->value))
             {
               /* Remove the matching change and re-check the current index */
-              svn_sort__array_delete(changes, i, 1);
+              SVN_ERR(svn_sort__array_delete2(changes, i, 1));
               i--;
             }
         }
     }
+  return SVN_NO_ERROR;
 }
 
 /* Get the empty file associated with the edit baton. This is cached so
@@ -1010,7 +1011,7 @@ close_file(void *file_baton,
         }
 
       if (fb->pristine_props)
-        remove_non_prop_changes(fb->pristine_props, fb->propchanges);
+        SVN_ERR(remove_non_prop_changes(fb->pristine_props, fb->propchanges));
 
       right_props = svn_prop__patch(fb->pristine_props, fb->propchanges,
                                     fb->pool);
@@ -1083,7 +1084,7 @@ close_directory(void *dir_baton,
 
       if (db->propchanges->nelts > 0)
         {
-          remove_non_prop_changes(pristine_props, db->propchanges);
+          SVN_ERR(remove_non_prop_changes(pristine_props, db->propchanges));
         }
 
       if (db->propchanges->nelts > 0 || db->added)

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/revert.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/revert.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/revert.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/revert.c Fri Jan 14 14:01:45 2022
@@ -57,24 +57,12 @@ struct revert_with_write_lock_baton {
 
 /* (Note: All arguments are in the baton above.)
 
-   Attempt to revert LOCAL_ABSPATH.
+   Attempt to revert LOCAL_ABSPATH by calling svn_wc_revert6(), which
+   see for further details.
 
-   If DEPTH is svn_depth_empty, revert just the properties on the
-   directory; else if svn_depth_files, revert the properties and any
-   files immediately under the directory; else if
-   svn_depth_immediates, revert all of the preceding plus properties
-   on immediate subdirectories; else if svn_depth_infinity, revert
-   path and everything under it fully recursively.
-
-   CHANGELISTS is an array of const char * changelist names, used as a
-   restrictive filter on items reverted; that is, don't revert any
-   item unless it's a member of one of those changelists.  If
-   CHANGELISTS is empty (or altogether NULL), no changelist filtering occurs.
-
-   Consult CTX to determine whether or not to revert timestamp to the
-   time of last commit ('use-commit-times = yes').
-
-   If PATH is unversioned, return SVN_ERR_UNVERSIONED_RESOURCE. */
+   If the target isn't versioned, send a 'skip' notification and return
+   no error.
+ */
 static svn_error_t *
 revert(void *baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool)
 {