1/*
2 * merge_elements.c: element-based merging
3 *
4 * ====================================================================
5 *    Licensed to the Apache Software Foundation (ASF) under one
6 *    or more contributor license agreements.  See the NOTICE file
7 *    distributed with this work for additional information
8 *    regarding copyright ownership.  The ASF licenses this file
9 *    to you under the Apache License, Version 2.0 (the
10 *    "License"); you may not use this file except in compliance
11 *    with the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 *    Unless required by applicable law or agreed to in writing,
16 *    software distributed under the License is distributed on an
17 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 *    KIND, either express or implied.  See the License for the
19 *    specific language governing permissions and limitations
20 *    under the License.
21 * ====================================================================
22 */
23
24#include <assert.h>
25#include <apr_strings.h>
26#include <apr_tables.h>
27#include <apr_hash.h>
28#include "svn_types.h"
29#include "svn_error.h"
30#include "svn_pools.h"
31#include "svn_hash.h"
32#include "svn_wc.h"
33#include "svn_client.h"
34#include "svn_dirent_uri.h"
35
36#include "client.h"
37#include "private/svn_element.h"
38
39#include "svn_private_config.h"
40
41
42/* Print a notification.
43 * ### TODO: Send notifications through ctx->notify_func2().
44 * ### TODO: Only when 'verbose' output is requested.
45 */
46static
47__attribute__((format(printf, 1, 2)))
48void
49verbose_notify(const char *fmt,
50               ...)
51{
52  va_list ap;
53
54  va_start(ap, fmt);
55  vprintf(fmt, ap);
56  if (fmt[strlen(fmt) - 1] != '\n')
57    printf("\n");
58  va_end(ap);
59}
60
61/* Return a string representation of PATHREV. */
62static const char *
63pathrev_str(const svn_client__pathrev_t *pathrev,
64            apr_pool_t *pool)
65{
66  const char *rrpath
67    = svn_uri_skip_ancestor(pathrev->repos_root_url, pathrev->url, pool);
68
69  return apr_psprintf(pool, "^/%s@%ld", rrpath, pathrev->rev);
70}
71
72/* Element matching info.
73 */
74typedef struct element_matching_info_t
75{
76  void *info;
77} element_matching_info_t;
78
79/* Return a string representation of INFO. */
80static const char *
81element_matching_info_str(const element_matching_info_t *info,
82                          apr_pool_t *result_pool)
83{
84  /* ### */
85  const char *str = "{...}";
86
87  return str;
88}
89
90/* Assign EIDs (in memory) to the source-left, source-right and target
91 * trees.
92 */
93static svn_error_t *
94assign_eids_to_trees(svn_element__tree_t **tree_left_p,
95                     svn_element__tree_t **tree_right_p,
96                     svn_element__tree_t **tree_target_p,
97                     const svn_client__pathrev_t *src_left,
98                     const svn_client__pathrev_t *src_right,
99                     merge_target_t *target,
100                     svn_ra_session_t *ra_session,
101                     element_matching_info_t *element_matching_info,
102                     svn_client_ctx_t *ctx,
103                     apr_pool_t *result_pool,
104                     apr_pool_t *scratch_pool)
105{
106  verbose_notify("--- Assigning EIDs to trees");
107
108  /* ### */
109  return SVN_NO_ERROR;
110}
111
112/* Perform a three-way tree merge. Write the result to *MERGE_RESULT_P.
113 *
114 * Set *CONFLICTS_P to describe any conflicts, or set *CONFLICTS_P to
115 * null if there are none.
116 */
117static svn_error_t *
118merge_trees(svn_element__tree_t **merge_result_p,
119            void **conflicts_p,
120            svn_element__tree_t *tree_left,
121            svn_element__tree_t *tree_right,
122            svn_element__tree_t *tree_target,
123            apr_pool_t *result_pool,
124            apr_pool_t *scratch_pool)
125{
126  verbose_notify("--- Merging trees");
127
128  /* ### */
129  *merge_result_p = NULL;
130  *conflicts_p = NULL;
131  return SVN_NO_ERROR;
132}
133
134/* Convert the MERGE_RESULT to a series of WC edits and apply those to
135 * the WC described in TARGET.
136 */
137static svn_error_t *
138apply_merge_result_to_wc(merge_target_t *target,
139                         svn_element__tree_t *merge_result,
140                         svn_client_ctx_t *ctx,
141                         apr_pool_t *scratch_pool)
142{
143  verbose_notify("--- Writing merge result to WC");
144
145  return SVN_NO_ERROR;
146}
147
148/* Do a three-way element-based merge for one merge source range,
149 * SRC_LEFT:SRC_RIGHT. If there are no conflicts, write the result to the
150 * WC described in TARGET.
151 */
152static svn_error_t *
153merge_elements_one_source(svn_boolean_t *use_sleep,
154                          const svn_client__pathrev_t *src_left,
155                          const svn_client__pathrev_t *src_right,
156                          merge_target_t *target,
157                          svn_ra_session_t *ra_session,
158                          element_matching_info_t *element_matching_info,
159                          svn_boolean_t diff_ignore_ancestry,
160                          svn_boolean_t force_delete,
161                          svn_boolean_t dry_run,
162                          const apr_array_header_t *merge_options,
163                          svn_client_ctx_t *ctx,
164                          apr_pool_t *scratch_pool)
165{
166  svn_element__tree_t *tree_left, *tree_right, *tree_target;
167  svn_element__tree_t *merge_result;
168  void *conflicts;
169
170  verbose_notify("--- Merging by elements: "
171                 "left=%s, right=%s, matching=%s",
172                 pathrev_str(src_left, scratch_pool),
173                 pathrev_str(src_right, scratch_pool),
174                 element_matching_info_str(element_matching_info,
175                                           scratch_pool));
176
177  /* assign EIDs (in memory) to the source-left, source-right and target
178     trees */
179  SVN_ERR(assign_eids_to_trees(&tree_left, &tree_right, &tree_target,
180                               src_left, src_right, target, ra_session,
181                               element_matching_info,
182                               ctx, scratch_pool, scratch_pool));
183
184  /* perform a tree merge, creating a temporary result (in memory) */
185  SVN_ERR(merge_trees(&merge_result, &conflicts,
186                      tree_left, tree_right, tree_target,
187                      scratch_pool, scratch_pool));
188
189  /* check for (new style) conflicts in the result; if any, bail out */
190  if (conflicts)
191    {
192      return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
193                              _("Merge had conflicts; "
194                                "this is not yet supported"));
195    }
196
197  /* convert the result to a series of WC edits and apply those to the WC */
198  if (dry_run)
199    {
200      verbose_notify("--- Dry run; not writing merge result to WC");
201    }
202  else
203    {
204      SVN_ERR(apply_merge_result_to_wc(target, merge_result,
205                                       ctx, scratch_pool));
206      *use_sleep = TRUE;
207    }
208
209  /* forget all the EID metadata */
210  return SVN_NO_ERROR;
211}
212
213svn_error_t *
214svn_client__merge_elements(svn_boolean_t *use_sleep,
215                           apr_array_header_t *merge_sources,
216                           merge_target_t *target,
217                           svn_ra_session_t *ra_session,
218                           svn_boolean_t diff_ignore_ancestry,
219                           svn_boolean_t force_delete,
220                           svn_boolean_t dry_run,
221                           const apr_array_header_t *merge_options,
222                           svn_client_ctx_t *ctx,
223                           apr_pool_t *result_pool,
224                           apr_pool_t *scratch_pool)
225{
226  int i;
227
228  /* Merge each source range in turn */
229  for (i = 0; i < merge_sources->nelts; i++)
230    {
231      merge_source_t *source
232        = APR_ARRAY_IDX(merge_sources, i, void *);
233      element_matching_info_t *element_matching_info;
234
235      /* ### TODO: get element matching info from the user */
236      element_matching_info = NULL;
237
238      SVN_ERR(merge_elements_one_source(use_sleep,
239                                        source->loc1, source->loc2,
240                                        target, ra_session,
241                                        element_matching_info,
242                                        diff_ignore_ancestry,
243                                        force_delete, dry_run, merge_options,
244                                        ctx, scratch_pool));
245    }
246
247  return SVN_NO_ERROR;
248}
249