Author: julianfoad
Date: Thu Dec 17 14:50:51 2015
New Revision: 1720560

URL: http://svn.apache.org/viewvc?rev=1720560&view=rev
Log:
Add skeleton code for a merge-by-elements feature.

See dev@ email thread "[RFC] An element-based 'svn merge'" started by me on
2015-12-17, archived at e.g. http://svn.haxx.se/dev/archive-2015-12/0061.shtml
or 
http://mail-archives.apache.org/mod_mbox/subversion-dev/201512.mbox/%3CCAEcU=1aurg2sy96ff5b0hbccolxkb1f67cdcms6nfz2d9cg...@mail.gmail.com%3E

This is conditional on an environment variable 'SVN_ELEMENT_MERGE' being
set.

* subversion/libsvn_client/client.h
  (merge_source_t,
   merge_target_t): Move these definitions to here from merge.c.
  (svn_client__merge_elements): New.

* subversion/libsvn_client/merge.c
  (merge_peg_locked): Call the merge-by-elements code under certain
    conditions.

* subversion/libsvn_client/merge_elements.c
  New file.

Added:
    subversion/trunk/subversion/libsvn_client/merge_elements.c   (with props)
Modified:
    subversion/trunk/subversion/libsvn_client/client.h
    subversion/trunk/subversion/libsvn_client/merge.c

Modified: subversion/trunk/subversion/libsvn_client/client.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/client.h?rev=1720560&r1=1720559&r2=1720560&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/client.h (original)
+++ subversion/trunk/subversion/libsvn_client/client.h Thu Dec 17 14:50:51 2015
@@ -1185,6 +1185,48 @@ svn_client__remote_propget(apr_hash_t *p
                            apr_pool_t *result_pool,
                            apr_pool_t *scratch_pool);
 
+/* */
+typedef struct merge_source_t
+{
+  /* "left" side URL and revision (inclusive iff youngest) */
+  const svn_client__pathrev_t *loc1;
+
+  /* "right" side URL and revision (inclusive iff youngest) */
+  const svn_client__pathrev_t *loc2;
+
+  /* True iff LOC1 is an ancestor of LOC2 or vice-versa (history-wise). */
+  svn_boolean_t ancestral;
+} merge_source_t;
+
+/* Description of the merge target root node (a WC working node) */
+typedef struct merge_target_t
+{
+  /* Absolute path to the WC node */
+  const char *abspath;
+
+  /* The repository location of the base node of the target WC.  If the node
+   * is locally added, then URL & REV are NULL & SVN_INVALID_REVNUM.
+   * REPOS_ROOT_URL and REPOS_UUID are always valid. */
+  svn_client__pathrev_t loc;
+
+} merge_target_t;
+
+/*
+ * Similar API to svn_client_merge_peg5().
+ */
+svn_error_t *
+svn_client__merge_elements(svn_boolean_t *use_sleep,
+                           apr_array_header_t *merge_sources,
+                           merge_target_t *target,
+                           svn_ra_session_t *ra_session,
+                           svn_boolean_t diff_ignore_ancestry,
+                           svn_boolean_t force_delete,
+                           svn_boolean_t dry_run,
+                           const apr_array_header_t *merge_options,
+                           svn_client_ctx_t *ctx,
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: subversion/trunk/subversion/libsvn_client/merge.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/merge.c?rev=1720560&r1=1720559&r2=1720560&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/merge.c (original)
+++ subversion/trunk/subversion/libsvn_client/merge.c Thu Dec 17 14:50:51 2015
@@ -215,32 +215,6 @@
 
 /*** Repos-Diff Editor Callbacks ***/
 
-/* */
-typedef struct merge_source_t
-{
-  /* "left" side URL and revision (inclusive iff youngest) */
-  const svn_client__pathrev_t *loc1;
-
-  /* "right" side URL and revision (inclusive iff youngest) */
-  const svn_client__pathrev_t *loc2;
-
-  /* True iff LOC1 is an ancestor of LOC2 or vice-versa (history-wise). */
-  svn_boolean_t ancestral;
-} merge_source_t;
-
-/* Description of the merge target root node (a WC working node) */
-typedef struct merge_target_t
-{
-  /* Absolute path to the WC node */
-  const char *abspath;
-
-  /* The repository location of the base node of the target WC.  If the node
-   * is locally added, then URL & REV are NULL & SVN_INVALID_REVNUM.
-   * REPOS_ROOT_URL and REPOS_UUID are always valid. */
-  svn_client__pathrev_t loc;
-
-} merge_target_t;
-
 typedef struct merge_cmd_baton_t {
   svn_boolean_t force_delete;         /* Delete a file/dir even if modified */
   svn_boolean_t dry_run;
@@ -11853,6 +11827,21 @@ merge_peg_locked(conflict_report_t **con
 
   /* Do the real merge!  (We say with confidence that our merge
      sources are both ancestral and related.) */
+  if (getenv("SVN_ELEMENT_MERGE")
+      && same_repos
+      && (depth == svn_depth_infinity || depth == svn_depth_unknown)
+      && ignore_mergeinfo
+      && !record_only)
+    {
+      err = svn_client__merge_elements(&use_sleep,
+                                       merge_sources, target, ra_session,
+                                       diff_ignore_ancestry, force_delete,
+                                       dry_run, merge_options,
+                                       ctx, result_pool, scratch_pool);
+      /* ### Currently this merge just errors out on any conflicts */
+      *conflict_report = NULL;
+    }
+  else
   err = do_merge(NULL, NULL, conflict_report, &use_sleep,
                  merge_sources, target, ra_session,
                  TRUE /*sources_related*/, same_repos, ignore_mergeinfo,

Added: subversion/trunk/subversion/libsvn_client/merge_elements.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/merge_elements.c?rev=1720560&view=auto
==============================================================================
--- subversion/trunk/subversion/libsvn_client/merge_elements.c (added)
+++ subversion/trunk/subversion/libsvn_client/merge_elements.c Thu Dec 17 
14:50:51 2015
@@ -0,0 +1,248 @@
+/*
+ * merge_elements.c: element-based merging
+ *
+ * ====================================================================
+ *    Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ * ====================================================================
+ */
+
+#include <assert.h>
+#include <apr_strings.h>
+#include <apr_tables.h>
+#include <apr_hash.h>
+#include "svn_types.h"
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "svn_hash.h"
+#include "svn_wc.h"
+#include "svn_client.h"
+#include "svn_dirent_uri.h"
+
+#include "client.h"
+#include "private/svn_element.h"
+
+#include "svn_private_config.h"
+
+
+/* Print a notification.
+ * ### TODO: Send notifications through ctx->notify_func2().
+ * ### TODO: Only when 'verbose' output is requested.
+ */
+static
+__attribute__((format(printf, 1, 2)))
+void
+verbose_notify(const char *fmt,
+               ...)
+{
+  va_list ap;
+
+  va_start(ap, fmt);
+  vprintf(fmt, ap);
+  if (fmt[strlen(fmt) - 1] != '\n')
+    printf("\n");
+  va_end(ap);
+}
+
+/* Return a string representation of PATHREV. */
+static const char *
+pathrev_str(const svn_client__pathrev_t *pathrev,
+            apr_pool_t *pool)
+{
+  const char *rrpath
+    = svn_uri_skip_ancestor(pathrev->repos_root_url, pathrev->url, pool);
+
+  return apr_psprintf(pool, "^/%s@%ld", rrpath, pathrev->rev);
+}
+
+/* Element matching info.
+ */
+typedef struct element_matching_info_t
+{
+  void *info;
+} element_matching_info_t;
+
+/* Return a string representation of INFO. */
+static const char *
+element_matching_info_str(const element_matching_info_t *info,
+                          apr_pool_t *result_pool)
+{
+  /* ### */
+  const char *str = "{...}";
+
+  return str;
+}
+
+/* Assign EIDs (in memory) to the source-left, source-right and target
+ * trees.
+ */
+static svn_error_t *
+assign_eids_to_trees(svn_element__tree_t **tree_left_p,
+                     svn_element__tree_t **tree_right_p,
+                     svn_element__tree_t **tree_target_p,
+                     const svn_client__pathrev_t *src_left,
+                     const svn_client__pathrev_t *src_right,
+                     merge_target_t *target,
+                     svn_ra_session_t *ra_session,
+                     element_matching_info_t *element_matching_info,
+                     svn_client_ctx_t *ctx,
+                     apr_pool_t *result_pool,
+                     apr_pool_t *scratch_pool)
+{
+  verbose_notify("--- Assigning EIDs to trees");
+
+  /* ### */
+  return SVN_NO_ERROR;
+}
+
+/* Perform a three-way tree merge. Write the result to *MERGE_RESULT_P.
+ *
+ * Set *CONFLICTS_P to describe any conflicts, or set *CONFLICTS_P to
+ * null if there are none.
+ */
+static svn_error_t *
+merge_trees(svn_element__tree_t **merge_result_p,
+            void **conflicts_p,
+            svn_element__tree_t *tree_left,
+            svn_element__tree_t *tree_right,
+            svn_element__tree_t *tree_target,
+            apr_pool_t *result_pool,
+            apr_pool_t *scratch_pool)
+{
+  verbose_notify("--- Merging trees");
+
+  /* ### */
+  *merge_result_p = NULL;
+  *conflicts_p = NULL;
+  return SVN_NO_ERROR;
+}
+
+/* Convert the MERGE_RESULT to a series of WC edits and apply those to
+ * the WC described in TARGET.
+ */
+static svn_error_t *
+apply_merge_result_to_wc(merge_target_t *target,
+                         svn_element__tree_t *merge_result,
+                         svn_client_ctx_t *ctx,
+                         apr_pool_t *scratch_pool)
+{
+  verbose_notify("--- Writing merge result to WC");
+
+  return SVN_NO_ERROR;
+}
+
+/* Do a three-way element-based merge for one merge source range,
+ * SRC_LEFT:SRC_RIGHT. If there are no conflicts, write the result to the
+ * WC described in TARGET.
+ */
+static svn_error_t *
+merge_elements_one_source(svn_boolean_t *use_sleep,
+                          const svn_client__pathrev_t *src_left,
+                          const svn_client__pathrev_t *src_right,
+                          merge_target_t *target,
+                          svn_ra_session_t *ra_session,
+                          element_matching_info_t *element_matching_info,
+                          svn_boolean_t diff_ignore_ancestry,
+                          svn_boolean_t force_delete,
+                          svn_boolean_t dry_run,
+                          const apr_array_header_t *merge_options,
+                          svn_client_ctx_t *ctx,
+                          apr_pool_t *scratch_pool)
+{
+  svn_element__tree_t *tree_left, *tree_right, *tree_target;
+  svn_element__tree_t *merge_result;
+  void *conflicts;
+
+  verbose_notify("--- Merging by elements: "
+                 "left=%s, right=%s, matching=%s",
+                 pathrev_str(src_left, scratch_pool),
+                 pathrev_str(src_right, scratch_pool),
+                 element_matching_info_str(element_matching_info,
+                                           scratch_pool));
+
+  /* assign EIDs (in memory) to the source-left, source-right and target
+     trees */
+  SVN_ERR(assign_eids_to_trees(&tree_left, &tree_right, &tree_target,
+                               src_left, src_right, target, ra_session,
+                               element_matching_info,
+                               ctx, scratch_pool, scratch_pool));
+
+  /* perform a tree merge, creating a temporary result (in memory) */
+  SVN_ERR(merge_trees(&merge_result, &conflicts,
+                      tree_left, tree_right, tree_target,
+                      scratch_pool, scratch_pool));
+
+  /* check for (new style) conflicts in the result; if any, bail out */
+  if (conflicts)
+    {
+      return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+                              _("Merge had conflicts; "
+                                "this is not yet supported"));
+    }
+
+  /* convert the result to a series of WC edits and apply those to the WC */
+  if (dry_run)
+    {
+      verbose_notify("--- Dry run; not writing merge result to WC");
+    }
+  else
+    {
+      SVN_ERR(apply_merge_result_to_wc(target, merge_result,
+                                       ctx, scratch_pool));
+      *use_sleep = TRUE;
+    }
+
+  /* forget all the EID metadata */
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__merge_elements(svn_boolean_t *use_sleep,
+                           apr_array_header_t *merge_sources,
+                           merge_target_t *target,
+                           svn_ra_session_t *ra_session,
+                           svn_boolean_t diff_ignore_ancestry,
+                           svn_boolean_t force_delete,
+                           svn_boolean_t dry_run,
+                           const apr_array_header_t *merge_options,
+                           svn_client_ctx_t *ctx,
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool)
+{
+  int i;
+
+  /* Merge each source range in turn */
+  for (i = 0; i < merge_sources->nelts; i++)
+    {
+      merge_source_t *source
+        = APR_ARRAY_IDX(merge_sources, i, void *);
+      element_matching_info_t *element_matching_info;
+
+      /* ### TODO: get element matching info from the user */
+      element_matching_info = NULL;
+
+      SVN_ERR(merge_elements_one_source(use_sleep,
+                                        source->loc1, source->loc2,
+                                        target, ra_session,
+                                        element_matching_info,
+                                        diff_ignore_ancestry,
+                                        force_delete, dry_run, merge_options,
+                                        ctx, scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}

Propchange: subversion/trunk/subversion/libsvn_client/merge_elements.c
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to