1 /*
2 * merge.c: 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 /* ==================================================================== */
25
26
27
28 /*** Includes ***/
29
30 #include <assert.h>
31 #include <apr_strings.h>
32 #include <apr_tables.h>
33 #include <apr_hash.h>
34 #include "svn_types.h"
35 #include "svn_hash.h"
36 #include "svn_wc.h"
37 #include "svn_delta.h"
38 #include "svn_diff.h"
39 #include "svn_mergeinfo.h"
40 #include "svn_client.h"
41 #include "svn_string.h"
42 #include "svn_error.h"
43 #include "svn_dirent_uri.h"
44 #include "svn_path.h"
45 #include "svn_io.h"
46 #include "svn_utf.h"
47 #include "svn_pools.h"
48 #include "svn_config.h"
49 #include "svn_props.h"
50 #include "svn_time.h"
51 #include "svn_sorts.h"
52 #include "svn_subst.h"
53 #include "svn_ra.h"
54 #include "client.h"
55 #include "mergeinfo.h"
56
57 #include "private/svn_fspath.h"
58 #include "private/svn_mergeinfo_private.h"
59 #include "private/svn_client_private.h"
60 #include "private/svn_sorts_private.h"
61 #include "private/svn_subr_private.h"
62 #include "private/svn_wc_private.h"
63
64 #include "svn_private_config.h"
65
66
67 /*-----------------------------------------------------------------------*/
68
69 /* MERGEINFO MERGE SOURCE NORMALIZATION
70 *
71 * Nearly any helper function herein that accepts two URL/revision
72 * pairs (or equivalent struct merge_source_t) expects one of two things
73 * to be true:
74 *
75 * 1. that mergeinfo is not being recorded at all for this
76 * operation, or
77 *
78 * 2. that the pairs represent two locations along a single line
79 * of version history such that there are no copies in the
80 * history of the object between the locations when treating
81 * the oldest of the two locations as non-inclusive. In other
82 * words, if there is a copy at all between them, there is only
83 * one copy and its source was the oldest of the two locations.
84 *
85 * We use svn_ra_get_location_segments() to split a given range of
86 * revisions across an object's history into several which obey these
87 * rules. For example, an extract from the log of Subversion's own
88 * /subversion/tags/1.4.5 directory shows the following copies between
89 * r859500 and r866500 (omitting the '/subversion' prefix for clarity):
90 *
91 * r859598:
92 * A /branches/1.4.x (from /trunk:859597)
93 *
94 * r865417:
95 * A /tags/1.4.4 (from /branches/1.4.x:865262)
96 * # Notice that this copy leaves a gap between 865262 and 865417.
97 *
98 * r866420:
99 * A /branches/1.4.5 (from /tags/1.4.4:866419)
100 *
101 * r866425:
102 * D /branches/1.4.5
103 * A /tags/1.4.5 (from /branches/1.4.5:866424)
104 *
105 * In graphical form:
106 *
107 * 859500 859597 865262 866419 866424 866500
108 * . . . . . .
109 * trunk ------------------------------------------------
110 * \ . . .
111 * branches/1.4.x A-------------------------------------
112 * . \______ . .
113 * . \ . .
114 * tags/1.4.4 . A-----------------------
115 * . . \ .
116 * branches/1.4.5 . . A------D
117 * . . . \.
118 * tags/1.4.5 . . . A---------
119 * . . . .
120 * 859598 865417 866420 866425
121 *
122 * A merge of the difference between r859500 and r866500 of this directory
123 * gets split into sequential merges of the following location pairs.
124 *
125 * 859500 859597 865262 865416 866419 866424 866500
126 * . . . . . . .
127 * trunk (======] . . . . .
128 * . . . . .
129 * trunk ( . . . . .
130 * branches/1.4.x ======] . . . .
131 * . . . .
132 * branches/1.4.x ( . . . .
133 * tags/1.4.4 =============] . .
134 * implicit_src_gap (======] . . .
135 * . . .
136 * tags/1.4.4 ( . .
137 * branches/1.4.5 ======] .
138 * . .
139 * branches/1.4.5 ( .
140 * tags/1.4.5 ======]
141 *
142 * which are represented in merge_source_t as:
143 *
144 * [/trunk:859500, /trunk:859597]
145 * (recorded in svn:mergeinfo as /trunk:859501-859597)
146 *
147 * [/trunk:859597, /branches/1.4.x:865262]
148 * (recorded in svn:mergeinfo as /branches/1.4.x:859598-865262)
149 *
150 * [/branches/1.4.x:865262, /tags/1.4.4@866419]
151 * (recorded in svn:mergeinfo as /tags/1.4.4:865263-866419)
152 * (and there is a gap, the revision range [865262, 865416])
153 *
154 * [/tags/1.4.4@866419, /branches/1.4.5@866424]
155 * (recorded in svn:mergeinfo as /branches/1.4.5:866420-866424)
156 *
157 * [/branches/1.4.5@866424, /tags/1.4.5@866500]
158 * (recorded in svn:mergeinfo as /tags/1.4.5:866425-866500)
159 *
160 * Our helper functions would then operate on one of these location
161 * pairs at a time.
162 */
163
164 /* WHICH SVN_CLIENT_MERGE* API DO I WANT?
165 *
166 * libsvn_client has three public merge APIs; they are all wrappers
167 * around the do_merge engine. Which one to use depends on the number
168 * of URLs passed as arguments and whether or not specific merge
169 * ranges (-c/-r) are specified.
170 *
171 * 1 URL 2 URLs
172 * +----+--------------------------------+---------------------+
173 * | -c | mergeinfo-driven | |
174 * | or | cherrypicking | |
175 * | -r | (svn_client_merge_peg) | |
176 * |----+--------------------------------+ |
177 * | | mergeinfo-driven | unsupported |
178 * | | 'cherry harvest', i.e. merge | |
179 * | | all revisions from URL that | |
180 * | no | have not already been merged | |
181 * | -c | (svn_client_merge_peg) | |
182 * | or +--------------------------------+---------------------+
183 * | -r | mergeinfo-driven | mergeinfo-writing |
184 * | | whole-branch | diff-and-apply |
185 * | | heuristic merge | (svn_client_merge) |
186 * | | (svn_client_merge_reintegrate) | |
187 * +----+--------------------------------+---------------------+
188 *
189 *
190 */
191
192 /* THE CHILDREN_WITH_MERGEINFO ARRAY
193 *
194 * Many of the helper functions in this file pass around an
195 * apr_array_header_t *CHILDREN_WITH_MERGEINFO. This is a depth first
196 * sorted array filled with svn_client__merge_path_t * describing the
197 * merge target and any of its subtrees which have explicit mergeinfo
198 * or otherwise need special attention during a merge.
199 *
200 * During mergeinfo unaware merges, CHILDREN_WITH_MERGEINFO contains
201 * contains only one element (added by do_mergeinfo_unaware_dir_merge)
202 * describing a contiguous range to be merged to the WC merge target.
203 *
204 * During mergeinfo aware merges CHILDREN_WITH_MERGEINFO is created
205 * by get_mergeinfo_paths() and outside of that function and its helpers
206 * should always meet the criteria dictated in get_mergeinfo_paths()'s doc
207 * string. The elements of CHILDREN_WITH_MERGEINFO should never be NULL.
208 *
209 * For clarification on mergeinfo aware vs. mergeinfo unaware merges, see
210 * the doc string for HONOR_MERGEINFO().
211 */
212
213
214 /*-----------------------------------------------------------------------*/
215
216 /*** Repos-Diff Editor Callbacks ***/
217
218 struct merge_cmd_baton_t;
219
220 struct notify_begin_state_t
221 {
222 /* Cache of which abspath was last notified. */
223 const char *last_abspath;
224
225 /* Reference to the main merge baton */
226 struct merge_cmd_baton_t *merge_b;
227
228 /* the wrapped notification callback */
229 svn_wc_notify_func2_t notify_func2;
230 void *notify_baton2;
231 };
232
233 typedef struct merge_cmd_baton_t {
234 svn_boolean_t force_delete; /* Delete a file/dir even if modified */
235 svn_boolean_t dry_run;
236 svn_boolean_t record_only; /* Whether to merge only mergeinfo
237 differences. */
238 svn_boolean_t same_repos; /* Whether the merge source repository
239 is the same repository as the
240 target. Defaults to FALSE if DRY_RUN
241 is TRUE.*/
242 svn_boolean_t mergeinfo_capable; /* Whether the merge source server
243 is capable of Merge Tracking. */
244 svn_boolean_t ignore_mergeinfo; /* Don't honor mergeinfo; see
245 doc string of do_merge(). FALSE if
246 MERGE_SOURCE->ancestral is FALSE. */
247 svn_boolean_t diff_ignore_ancestry; /* Diff unrelated nodes as if related; see
248 doc string of do_merge(). FALSE if
249 MERGE_SOURCE->ancestral is FALSE. */
250 svn_boolean_t reintegrate_merge; /* Whether this is a --reintegrate
251 merge or not. */
252 const merge_target_t *target; /* Description of merge target node */
253
254 /* The left and right URLs and revs. The value of this field changes to
255 reflect the merge_source_t *currently* being merged by do_merge(). */
256 merge_source_t merge_source;
257
258 /* Rangelist containing single range which describes the gap, if any,
259 in the natural history of the merge source currently being processed.
260 See https://issues.apache.org/jira/browse/SVN-3432.
261 Updated during each call to do_directory_merge(). May be NULL if there
262 is no gap. */
263 svn_rangelist_t *implicit_src_gap;
264
265 /* Reference to the one-and-only CHILDREN_WITH_MERGEINFO (see global
266 comment) or a similar list for single-file-merges */
267 apr_array_header_t *children_with_mergeinfo;
268
269 svn_client_ctx_t *ctx; /* Client context for callbacks, etc. */
270
271 /* The list of any paths which remained in conflict after a
272 resolution attempt was made. We track this in-memory, rather
273 than just using WC entry state, since the latter doesn't help us
274 when in dry_run mode.
275 ### And because we only want to resolve conflicts that were
276 generated by this merge, not pre-existing ones? */
277 apr_hash_t *conflicted_paths;
278
279 /* A list of absolute paths which had no explicit mergeinfo prior to the
280 merge but got explicit mergeinfo added by the merge. This is populated
281 by merge_change_props() and is allocated in POOL so it is subject to the
282 lifetime limitations of POOL. Is NULL if no paths are found which
283 meet the criteria or DRY_RUN is true. */
284 apr_hash_t *paths_with_new_mergeinfo;
285
286 /* A list of absolute paths whose mergeinfo doesn't need updating after
287 the merge. This can be caused by the removal of mergeinfo by the merge
288 or by deleting the node itself. This is populated by merge_change_props()
289 and the delete callbacks and is allocated in POOL so it is subject to the
290 lifetime limitations of POOL. Is NULL if no paths are found which
291 meet the criteria or DRY_RUN is true. */
292 apr_hash_t *paths_with_deleted_mergeinfo;
293
294 /* The list of absolute skipped paths, which should be examined and
295 cleared after each invocation of the callback. The paths
296 are absolute. Is NULL if MERGE_B->MERGE_SOURCE->ancestral and
297 MERGE_B->REINTEGRATE_MERGE are both false. */
298 apr_hash_t *skipped_abspaths;
299
300 /* The list of absolute merged paths. Unused if MERGE_B->MERGE_SOURCE->ancestral
301 and MERGE_B->REINTEGRATE_MERGE are both false. */
302 apr_hash_t *merged_abspaths;
303
304 /* A hash of (const char *) absolute WC paths mapped to the same which
305 represent the roots of subtrees added by the merge. */
306 apr_hash_t *added_abspaths;
307
308 /* A list of tree conflict victim absolute paths which may be NULL. */
309 apr_hash_t *tree_conflicted_abspaths;
310
311 /* The diff3_cmd in ctx->config, if any, else null. We could just
312 extract this as needed, but since more than one caller uses it,
313 we just set it up when this baton is created. */
314 const char *diff3_cmd;
315 const apr_array_header_t *merge_options;
316
317 /* Array of file extension patterns to preserve as extensions in
318 generated conflict files. */
319 const apr_array_header_t *ext_patterns;
320
321 /* RA sessions used throughout a merge operation. Opened/re-parented
322 as needed.
323
324 NOTE: During the actual merge editor drive, RA_SESSION1 is used
325 for the primary editing and RA_SESSION2 for fetching additional
326 information -- as necessary -- from the repository. So during
327 this phase of the merge, you *must not* reparent RA_SESSION1; use
328 (temporarily reparenting if you must) RA_SESSION2 instead. */
329 svn_ra_session_t *ra_session1;
330 svn_ra_session_t *ra_session2;
331
332 /* During the merge, *USE_SLEEP is set to TRUE if a sleep will be required
333 afterwards to ensure timestamp integrity, or unchanged if not. */
334 svn_boolean_t *use_sleep;
335
336 /* Pool which has a lifetime limited to one iteration over a given
337 merge source, i.e. it is cleared on every call to do_directory_merge()
338 or do_file_merge() in do_merge(). */
339 apr_pool_t *pool;
340
341 /* Our notification callback, that adds a 'begin' notification */
342 svn_wc_notify_func2_t notify_func;
343 void *notify_baton;
344 struct notify_begin_state_t notify_begin;
345
346 } merge_cmd_baton_t;
347
348
349 /* Return TRUE iff we should be taking account of mergeinfo in deciding what
350 changes to merge, for the merge described by MERGE_B. Specifically, that
351 is if the merge source server is capable of merge tracking, the left-side
352 merge source is an ancestor of the right-side (or vice-versa), the merge
353 source is in the same repository as the merge target, and we are not
354 ignoring mergeinfo. */
355 static svn_boolean_t
HONOR_MERGEINFO(const merge_cmd_baton_t * merge_b)356 HONOR_MERGEINFO(const merge_cmd_baton_t *merge_b)
357 {
358 return (merge_b->mergeinfo_capable
359 && merge_b->merge_source.ancestral
360 && merge_b->same_repos
361 && (!merge_b->ignore_mergeinfo));
362 }
363
364
365 /* Return TRUE iff we should be recording mergeinfo for the merge described
366 by MERGE_B. Specifically, that is if we are honoring mergeinfo and the
367 merge is not a dry run. */
368 static svn_boolean_t
RECORD_MERGEINFO(const merge_cmd_baton_t * merge_b)369 RECORD_MERGEINFO(const merge_cmd_baton_t *merge_b)
370 {
371 return (HONOR_MERGEINFO(merge_b)
372 && !merge_b->dry_run);
373 }
374
375
376 /*-----------------------------------------------------------------------*/
377
378 /*** Utilities ***/
379
380 /* Return TRUE iff the session URL of RA_SESSION is equal to URL. Useful in
381 * asserting preconditions. */
382 static svn_boolean_t
session_url_is(svn_ra_session_t * ra_session,const char * url,apr_pool_t * scratch_pool)383 session_url_is(svn_ra_session_t *ra_session,
384 const char *url,
385 apr_pool_t *scratch_pool)
386 {
387 const char *session_url;
388 svn_error_t *err
389 = svn_ra_get_session_url(ra_session, &session_url, scratch_pool);
390
391 SVN_ERR_ASSERT_NO_RETURN(! err);
392 return strcmp(url, session_url) == 0;
393 }
394
395 /* Return a new merge_source_t structure, allocated in RESULT_POOL,
396 * initialized with deep copies of LOC1 and LOC2 and ANCESTRAL. */
397 static merge_source_t *
merge_source_create(const svn_client__pathrev_t * loc1,const svn_client__pathrev_t * loc2,svn_boolean_t ancestral,apr_pool_t * result_pool)398 merge_source_create(const svn_client__pathrev_t *loc1,
399 const svn_client__pathrev_t *loc2,
400 svn_boolean_t ancestral,
401 apr_pool_t *result_pool)
402 {
403 merge_source_t *s
404 = apr_palloc(result_pool, sizeof(*s));
405
406 s->loc1 = svn_client__pathrev_dup(loc1, result_pool);
407 s->loc2 = svn_client__pathrev_dup(loc2, result_pool);
408 s->ancestral = ancestral;
409 return s;
410 }
411
412 /* Return a deep copy of SOURCE, allocated in RESULT_POOL. */
413 static merge_source_t *
merge_source_dup(const merge_source_t * source,apr_pool_t * result_pool)414 merge_source_dup(const merge_source_t *source,
415 apr_pool_t *result_pool)
416 {
417 merge_source_t *s = apr_palloc(result_pool, sizeof(*s));
418
419 s->loc1 = svn_client__pathrev_dup(source->loc1, result_pool);
420 s->loc2 = svn_client__pathrev_dup(source->loc2, result_pool);
421 s->ancestral = source->ancestral;
422 return s;
423 }
424
425 /* Return SVN_ERR_UNSUPPORTED_FEATURE if URL is not inside the repository
426 of LOCAL_ABSPATH. Use SCRATCH_POOL for temporary allocations. */
427 static svn_error_t *
check_repos_match(const merge_target_t * target,const char * local_abspath,const char * url,apr_pool_t * scratch_pool)428 check_repos_match(const merge_target_t *target,
429 const char *local_abspath,
430 const char *url,
431 apr_pool_t *scratch_pool)
432 {
433 if (!svn_uri__is_ancestor(target->loc.repos_root_url, url))
434 return svn_error_createf(
435 SVN_ERR_UNSUPPORTED_FEATURE, NULL,
436 _("URL '%s' of '%s' is not in repository '%s'"),
437 url, svn_dirent_local_style(local_abspath, scratch_pool),
438 target->loc.repos_root_url);
439
440 return SVN_NO_ERROR;
441 }
442
443 /* Return TRUE iff the repository of LOCATION1 is the same as
444 * that of LOCATION2. If STRICT_URLS is true, the URLs must
445 * match (and the UUIDs, just to be sure), otherwise just the UUIDs must
446 * match and the URLs can differ (a common case is http versus https). */
447 static svn_boolean_t
is_same_repos(const svn_client__pathrev_t * location1,const svn_client__pathrev_t * location2,svn_boolean_t strict_urls)448 is_same_repos(const svn_client__pathrev_t *location1,
449 const svn_client__pathrev_t *location2,
450 svn_boolean_t strict_urls)
451 {
452 if (strict_urls)
453 return (strcmp(location1->repos_root_url, location2->repos_root_url) == 0
454 && strcmp(location1->repos_uuid, location2->repos_uuid) == 0);
455 else
456 return (strcmp(location1->repos_uuid, location2->repos_uuid) == 0);
457 }
458
459 /* If the repository identified of LOCATION1 is not the same as that
460 * of LOCATION2, throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES
461 * error mentioning PATH1 and PATH2. For STRICT_URLS, see is_same_repos().
462 */
463 static svn_error_t *
check_same_repos(const svn_client__pathrev_t * location1,const char * path1,const svn_client__pathrev_t * location2,const char * path2,svn_boolean_t strict_urls,apr_pool_t * scratch_pool)464 check_same_repos(const svn_client__pathrev_t *location1,
465 const char *path1,
466 const svn_client__pathrev_t *location2,
467 const char *path2,
468 svn_boolean_t strict_urls,
469 apr_pool_t *scratch_pool)
470 {
471 if (! is_same_repos(location1, location2, strict_urls))
472 return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
473 _("'%s' must be from the same repository as "
474 "'%s'"), path1, path2);
475 return SVN_NO_ERROR;
476 }
477
478 /* Store LOCAL_ABSPATH in PATH_HASH after duplicating it into the pool
479 containing PATH_HASH. */
480 static APR_INLINE void
store_path(apr_hash_t * path_hash,const char * local_abspath)481 store_path(apr_hash_t *path_hash, const char *local_abspath)
482 {
483 const char *dup_path = apr_pstrdup(apr_hash_pool_get(path_hash),
484 local_abspath);
485
486 svn_hash_sets(path_hash, dup_path, dup_path);
487 }
488
489 /* Store LOCAL_ABSPATH in *PATH_HASH_P after duplicating it into the pool
490 containing *PATH_HASH_P. If *PATH_HASH_P is NULL, then first set
491 *PATH_HASH_P to a new hash allocated from POOL. */
492 static APR_INLINE void
alloc_and_store_path(apr_hash_t ** path_hash_p,const char * local_abspath,apr_pool_t * pool)493 alloc_and_store_path(apr_hash_t **path_hash_p,
494 const char *local_abspath,
495 apr_pool_t *pool)
496 {
497 if (! *path_hash_p)
498 *path_hash_p = apr_hash_make(pool);
499 store_path(*path_hash_p, local_abspath);
500 }
501
502 /* Return whether any WC path was put in conflict by the merge
503 operation corresponding to MERGE_B. */
504 static APR_INLINE svn_boolean_t
is_path_conflicted_by_merge(merge_cmd_baton_t * merge_b)505 is_path_conflicted_by_merge(merge_cmd_baton_t *merge_b)
506 {
507 return (merge_b->conflicted_paths &&
508 apr_hash_count(merge_b->conflicted_paths) > 0);
509 }
510
511 /* Return a state indicating whether the WC metadata matches the
512 * node kind on disk of the local path LOCAL_ABSPATH.
513 * Use MERGE_B to determine the dry-run details; particularly, if a dry run
514 * noted that it deleted this path, assume matching node kinds (as if both
515 * kinds were svn_node_none).
516 *
517 * - Return svn_wc_notify_state_inapplicable if the node kind matches.
518 * - Return 'obstructed' if there is a node on disk where none or a
519 * different kind is expected, or if the disk node cannot be read.
520 * - Return 'missing' if there is no node on disk but one is expected.
521 * Also return 'missing' for server-excluded nodes (not here due to
522 * authz or other reasons determined by the server).
523 *
524 * Optionally return a bit more info for interested users.
525 **/
526 static svn_error_t *
perform_obstruction_check(svn_wc_notify_state_t * obstruction_state,svn_boolean_t * deleted,svn_boolean_t * excluded,svn_node_kind_t * kind,svn_depth_t * parent_depth,const merge_cmd_baton_t * merge_b,const char * local_abspath,apr_pool_t * scratch_pool)527 perform_obstruction_check(svn_wc_notify_state_t *obstruction_state,
528 svn_boolean_t *deleted,
529 svn_boolean_t *excluded,
530 svn_node_kind_t *kind,
531 svn_depth_t *parent_depth,
532 const merge_cmd_baton_t *merge_b,
533 const char *local_abspath,
534 apr_pool_t *scratch_pool)
535 {
536 svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
537 svn_node_kind_t wc_kind;
538 svn_boolean_t check_root;
539
540 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
541
542 *obstruction_state = svn_wc_notify_state_inapplicable;
543
544 if (deleted)
545 *deleted = FALSE;
546 if (kind)
547 *kind = svn_node_none;
548
549 if (kind == NULL)
550 kind = &wc_kind;
551
552 check_root = ! strcmp(local_abspath, merge_b->target->abspath);
553
554 SVN_ERR(svn_wc__check_for_obstructions(obstruction_state,
555 kind,
556 deleted,
557 excluded,
558 parent_depth,
559 wc_ctx, local_abspath,
560 check_root,
561 scratch_pool));
562 return SVN_NO_ERROR;
563 }
564
565 /* Create *LEFT and *RIGHT conflict versions for conflict victim
566 * at VICTIM_ABSPATH, with merge-left node kind MERGE_LEFT_NODE_KIND
567 * and merge-right node kind MERGE_RIGHT_NODE_KIND, using information
568 * obtained from MERGE_SOURCE and TARGET.
569 * Allocate returned conflict versions in RESULT_POOL. */
570 static svn_error_t *
make_conflict_versions(const svn_wc_conflict_version_t ** left,const svn_wc_conflict_version_t ** right,const char * victim_abspath,svn_node_kind_t merge_left_node_kind,svn_node_kind_t merge_right_node_kind,const merge_source_t * merge_source,const merge_target_t * target,apr_pool_t * result_pool,apr_pool_t * scratch_pool)571 make_conflict_versions(const svn_wc_conflict_version_t **left,
572 const svn_wc_conflict_version_t **right,
573 const char *victim_abspath,
574 svn_node_kind_t merge_left_node_kind,
575 svn_node_kind_t merge_right_node_kind,
576 const merge_source_t *merge_source,
577 const merge_target_t *target,
578 apr_pool_t *result_pool,
579 apr_pool_t *scratch_pool)
580 {
581 const char *child = svn_dirent_skip_ancestor(target->abspath,
582 victim_abspath);
583 const char *left_relpath, *right_relpath;
584
585 SVN_ERR_ASSERT(child != NULL);
586 left_relpath = svn_client__pathrev_relpath(merge_source->loc1,
587 scratch_pool);
588 right_relpath = svn_client__pathrev_relpath(merge_source->loc2,
589 scratch_pool);
590
591 *left = svn_wc_conflict_version_create2(
592 merge_source->loc1->repos_root_url,
593 merge_source->loc1->repos_uuid,
594 svn_relpath_join(left_relpath, child, scratch_pool),
595 merge_source->loc1->rev,
596 merge_left_node_kind, result_pool);
597
598 *right = svn_wc_conflict_version_create2(
599 merge_source->loc2->repos_root_url,
600 merge_source->loc2->repos_uuid,
601 svn_relpath_join(right_relpath, child, scratch_pool),
602 merge_source->loc2->rev,
603 merge_right_node_kind, result_pool);
604
605 return SVN_NO_ERROR;
606 }
607
608 /* Helper for filter_self_referential_mergeinfo()
609
610 *MERGEINFO is a non-empty, non-null collection of mergeinfo.
611
612 Remove all mergeinfo from *MERGEINFO that describes revision ranges
613 greater than REVISION. Put a copy of any removed mergeinfo, allocated
614 in POOL, into *YOUNGER_MERGEINFO.
615
616 If no mergeinfo is removed from *MERGEINFO then *YOUNGER_MERGEINFO is set
617 to NULL. If all mergeinfo is removed from *MERGEINFO then *MERGEINFO is
618 set to NULL.
619 */
620 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)621 split_mergeinfo_on_revision(svn_mergeinfo_t *younger_mergeinfo,
622 svn_mergeinfo_t *mergeinfo,
623 svn_revnum_t revision,
624 apr_pool_t *pool)
625 {
626 apr_hash_index_t *hi;
627 apr_pool_t *iterpool = svn_pool_create(pool);
628
629 *younger_mergeinfo = NULL;
630 for (hi = apr_hash_first(pool, *mergeinfo); hi; hi = apr_hash_next(hi))
631 {
632 int i;
633 const char *merge_source_path = apr_hash_this_key(hi);
634 svn_rangelist_t *rangelist = apr_hash_this_val(hi);
635
636 svn_pool_clear(iterpool);
637
638 for (i = 0; i < rangelist->nelts; i++)
639 {
640 svn_merge_range_t *range =
641 APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
642 if (range->end <= revision)
643 {
644 /* This entirely of this range is as old or older than
645 REVISION, so leave it in *MERGEINFO. */
646 continue;
647 }
648 else
649 {
650 /* Since the rangelists in svn_mergeinfo_t's are sorted in
651 increasing order we know that part or all of *this* range
652 and *all* of the remaining ranges in *RANGELIST are younger
653 than REVISION. Remove the younger rangelists from
654 *MERGEINFO and put them in *YOUNGER_MERGEINFO. */
655 int j;
656 svn_rangelist_t *younger_rangelist =
657 apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
658
659 for (j = i; j < rangelist->nelts; j++)
660 {
661 svn_merge_range_t *younger_range = svn_merge_range_dup(
662 APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *), pool);
663
664 /* REVISION might intersect with the first range where
665 range->end > REVISION. If that is the case then split
666 the current range into two, putting the younger half
667 into *YOUNGER_MERGEINFO and leaving the older half in
668 *MERGEINFO. */
669 if (j == i && range->start + 1 <= revision)
670 younger_range->start = range->end = revision;
671
672 APR_ARRAY_PUSH(younger_rangelist, svn_merge_range_t *) =
673 younger_range;
674 }
675
676 /* So far we've only been manipulating rangelists, now we
677 actually create *YOUNGER_MERGEINFO and then remove the older
678 ranges from *MERGEINFO */
679 if (!(*younger_mergeinfo))
680 *younger_mergeinfo = apr_hash_make(pool);
681 svn_hash_sets(*younger_mergeinfo, merge_source_path,
682 younger_rangelist);
683 SVN_ERR(svn_mergeinfo_remove2(mergeinfo, *younger_mergeinfo,
684 *mergeinfo, TRUE, pool, iterpool));
685 break; /* ...out of for (i = 0; i < rangelist->nelts; i++) */
686 }
687 }
688 }
689
690 svn_pool_destroy(iterpool);
691
692 return SVN_NO_ERROR;
693 }
694
695
696 /* Make a copy of PROPCHANGES (array of svn_prop_t) into *TRIMMED_PROPCHANGES,
697 omitting any svn:mergeinfo changes. */
698 static svn_error_t *
omit_mergeinfo_changes(apr_array_header_t ** trimmed_propchanges,const apr_array_header_t * propchanges,apr_pool_t * result_pool)699 omit_mergeinfo_changes(apr_array_header_t **trimmed_propchanges,
700 const apr_array_header_t *propchanges,
701 apr_pool_t *result_pool)
702 {
703 int i;
704
705 *trimmed_propchanges = apr_array_make(result_pool,
706 propchanges->nelts,
707 sizeof(svn_prop_t));
708
709 for (i = 0; i < propchanges->nelts; ++i)
710 {
711 const svn_prop_t *change = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
712
713 /* If this property is not svn:mergeinfo, then copy it. */
714 if (strcmp(change->name, SVN_PROP_MERGEINFO) != 0)
715 APR_ARRAY_PUSH(*trimmed_propchanges, svn_prop_t) = *change;
716 }
717
718 return SVN_NO_ERROR;
719 }
720
721
722 /* Helper for merge_props_changed().
723
724 *PROPS is an array of svn_prop_t structures representing regular properties
725 to be added to the working copy TARGET_ABSPATH.
726
727 The merge source and target are assumed to be in the same repository.
728
729 Filter out mergeinfo property additions to TARGET_ABSPATH when
730 those additions refer to the same line of history as TARGET_ABSPATH as
731 described below.
732
733 Examine the added mergeinfo, looking at each range (or single rev)
734 of each source path. If a source_path/range refers to the same line of
735 history as TARGET_ABSPATH (pegged at its base revision), then filter out
736 that range. If the entire rangelist for a given path is filtered then
737 filter out the path as well.
738
739 RA_SESSION is an open RA session to the repository
740 in which both the source and target live, else RA_SESSION is not used. It
741 may be temporarily reparented as needed by this function.
742
743 Use CTX for any further client operations.
744
745 If any filtering occurs, set outgoing *PROPS to a shallow copy (allocated
746 in POOL) of incoming *PROPS minus the filtered mergeinfo. */
747 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)748 filter_self_referential_mergeinfo(apr_array_header_t **props,
749 const char *target_abspath,
750 svn_ra_session_t *ra_session,
751 svn_client_ctx_t *ctx,
752 apr_pool_t *pool)
753 {
754 apr_array_header_t *adjusted_props;
755 int i;
756 apr_pool_t *iterpool;
757 svn_boolean_t is_copy;
758 const char *repos_relpath;
759 svn_client__pathrev_t target_base;
760
761 /* If PATH itself has been added there is no need to filter. */
762 SVN_ERR(svn_wc__node_get_origin(&is_copy, &target_base.rev, &repos_relpath,
763 &target_base.repos_root_url,
764 &target_base.repos_uuid, NULL, NULL,
765 ctx->wc_ctx, target_abspath, FALSE,
766 pool, pool));
767
768 if (is_copy || !repos_relpath)
769 return SVN_NO_ERROR; /* A copy or a local addition */
770
771 target_base.url = svn_path_url_add_component2(target_base.repos_root_url,
772 repos_relpath, pool);
773
774 adjusted_props = apr_array_make(pool, (*props)->nelts, sizeof(svn_prop_t));
775 iterpool = svn_pool_create(pool);
776 for (i = 0; i < (*props)->nelts; ++i)
777 {
778 svn_prop_t *prop = &APR_ARRAY_IDX((*props), i, svn_prop_t);
779
780 svn_mergeinfo_t mergeinfo, younger_mergeinfo;
781 svn_mergeinfo_t filtered_mergeinfo = NULL;
782 svn_mergeinfo_t filtered_younger_mergeinfo = NULL;
783 svn_error_t *err;
784
785 /* If this property isn't mergeinfo or is NULL valued (i.e. prop removal)
786 or empty mergeinfo it does not require any special handling. There
787 is nothing to filter out of empty mergeinfo and the concept of
788 filtering doesn't apply if we are trying to remove mergeinfo
789 entirely. */
790 if ((strcmp(prop->name, SVN_PROP_MERGEINFO) != 0)
791 || (! prop->value) /* Removal of mergeinfo */
792 || (! prop->value->len)) /* Empty mergeinfo */
793 {
794 APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
795 continue;
796 }
797
798 svn_pool_clear(iterpool);
799
800 /* Non-empty mergeinfo; filter self-referential mergeinfo out. */
801
802 /* Parse the incoming mergeinfo to allow easier manipulation. */
803 err = svn_mergeinfo_parse(&mergeinfo, prop->value->data, iterpool);
804
805 if (err)
806 {
807 /* Issue #3896: If we can't parse it, we certainly can't
808 filter it. */
809 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
810 {
811 svn_error_clear(err);
812 APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
813 continue;
814 }
815 else
816 {
817 return svn_error_trace(err);
818 }
819 }
820
821 /* The working copy target PATH is at BASE_REVISION. Divide the
822 incoming mergeinfo into two groups. One where all revision ranges
823 are as old or older than BASE_REVISION and one where all revision
824 ranges are younger.
825
826 Note: You may be wondering why we do this.
827
828 For the incoming mergeinfo "older" than target's base revision we
829 can filter out self-referential mergeinfo efficiently using
830 svn_client__get_history_as_mergeinfo(). We simply look at PATH's
831 natural history as mergeinfo and remove that from any incoming
832 mergeinfo.
833
834 For mergeinfo "younger" than the base revision we can't use
835 svn_ra_get_location_segments() to look into PATH's future
836 history. Instead we must use svn_client__repos_locations() and
837 look at each incoming source/range individually and see if PATH
838 at its base revision and PATH at the start of the incoming range
839 exist on the same line of history. If they do then we can filter
840 out the incoming range. But since we have to do this for each
841 range there is a substantial performance penalty to pay if the
842 incoming ranges are not contiguous, i.e. we call
843 svn_client__repos_locations for each discrete range and incur
844 the cost of a roundtrip communication with the repository. */
845 SVN_ERR(split_mergeinfo_on_revision(&younger_mergeinfo,
846 &mergeinfo,
847 target_base.rev,
848 iterpool));
849
850 /* Filter self-referential mergeinfo from younger_mergeinfo. */
851 if (younger_mergeinfo)
852 {
853 apr_hash_index_t *hi;
854 const char *merge_source_root_url;
855
856 SVN_ERR(svn_ra_get_repos_root2(ra_session,
857 &merge_source_root_url, iterpool));
858
859 for (hi = apr_hash_first(iterpool, younger_mergeinfo);
860 hi; hi = apr_hash_next(hi))
861 {
862 int j;
863 const char *source_path = apr_hash_this_key(hi);
864 svn_rangelist_t *rangelist = apr_hash_this_val(hi);
865 const char *merge_source_url;
866 svn_rangelist_t *adjusted_rangelist =
867 apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
868
869 merge_source_url =
870 svn_path_url_add_component2(merge_source_root_url,
871 source_path + 1, iterpool);
872
873 for (j = 0; j < rangelist->nelts; j++)
874 {
875 svn_error_t *err2;
876 svn_client__pathrev_t *start_loc;
877 svn_merge_range_t *range =
878 APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *);
879
880 /* Because the merge source normalization code
881 ensures mergeinfo refers to real locations on
882 the same line of history, there's no need to
883 look at the whole range, just the start. */
884
885 /* Check if PATH@BASE_REVISION exists at
886 RANGE->START on the same line of history.
887 (start+1 because RANGE->start is not inclusive.) */
888 err2 = svn_client__repos_location(&start_loc, ra_session,
889 &target_base,
890 range->start + 1,
891 ctx, iterpool, iterpool);
892 if (err2)
893 {
894 if (err2->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES
895 || err2->apr_err == SVN_ERR_FS_NOT_FOUND
896 || err2->apr_err == SVN_ERR_FS_NO_SUCH_REVISION)
897 {
898 /* PATH@BASE_REVISION didn't exist at
899 RANGE->START + 1 or is unrelated to the
900 resource PATH@RANGE->START. Some of the
901 requested revisions may not even exist in
902 the repository; a real possibility since
903 mergeinfo is hand editable. In all of these
904 cases clear and ignore the error and don't
905 do any filtering.
906
907 Note: In this last case it is possible that
908 we will allow self-referential mergeinfo to
909 be applied, but fixing it here is potentially
910 very costly in terms of finding what part of
911 a range is actually valid. Simply allowing
912 the merge to proceed without filtering the
913 offending range seems the least worst
914 option. */
915 svn_error_clear(err2);
916 err2 = NULL;
917 APR_ARRAY_PUSH(adjusted_rangelist,
918 svn_merge_range_t *) = range;
919 }
920 else
921 {
922 return svn_error_trace(err2);
923 }
924 }
925 else
926 {
927 /* PATH@BASE_REVISION exists on the same
928 line of history at RANGE->START and RANGE->END.
929 Now check that PATH@BASE_REVISION's path
930 names at RANGE->START and RANGE->END are the same.
931 If the names are not the same then the mergeinfo
932 describing PATH@RANGE->START through
933 PATH@RANGE->END actually belong to some other
934 line of history and we want to record this
935 mergeinfo, not filter it. */
936 if (strcmp(start_loc->url, merge_source_url) != 0)
937 {
938 APR_ARRAY_PUSH(adjusted_rangelist,
939 svn_merge_range_t *) = range;
940 }
941 }
942 /* else no need to add, this mergeinfo is
943 all on the same line of history. */
944 } /* for (j = 0; j < rangelist->nelts; j++) */
945
946 /* Add any rangelists for source_path that are not
947 self-referential. */
948 if (adjusted_rangelist->nelts)
949 {
950 if (!filtered_younger_mergeinfo)
951 filtered_younger_mergeinfo = apr_hash_make(iterpool);
952 svn_hash_sets(filtered_younger_mergeinfo, source_path,
953 adjusted_rangelist);
954 }
955
956 } /* Iteration over each merge source in younger_mergeinfo. */
957 } /* if (younger_mergeinfo) */
958
959 /* Filter self-referential mergeinfo from "older" mergeinfo. */
960 if (mergeinfo)
961 {
962 svn_mergeinfo_t implicit_mergeinfo;
963
964 SVN_ERR(svn_client__get_history_as_mergeinfo(
965 &implicit_mergeinfo, NULL,
966 &target_base, target_base.rev, SVN_INVALID_REVNUM,
967 ra_session, ctx, iterpool));
968
969 /* Remove PATH's implicit mergeinfo from the incoming mergeinfo. */
970 SVN_ERR(svn_mergeinfo_remove2(&filtered_mergeinfo,
971 implicit_mergeinfo,
972 mergeinfo, TRUE, iterpool, iterpool));
973 }
974
975 /* Combine whatever older and younger filtered mergeinfo exists
976 into filtered_mergeinfo. */
977 if (filtered_mergeinfo && filtered_younger_mergeinfo)
978 SVN_ERR(svn_mergeinfo_merge2(filtered_mergeinfo,
979 filtered_younger_mergeinfo, iterpool,
980 iterpool));
981 else if (filtered_younger_mergeinfo)
982 filtered_mergeinfo = filtered_younger_mergeinfo;
983
984 /* If there is any incoming mergeinfo remaining after filtering
985 then put it in adjusted_props. */
986 if (filtered_mergeinfo && apr_hash_count(filtered_mergeinfo))
987 {
988 /* Convert filtered_mergeinfo to a svn_prop_t and put it
989 back in the array. */
990 svn_string_t *filtered_mergeinfo_str;
991 svn_prop_t *adjusted_prop = apr_pcalloc(pool,
992 sizeof(*adjusted_prop));
993 SVN_ERR(svn_mergeinfo_to_string(&filtered_mergeinfo_str,
994 filtered_mergeinfo,
995 pool));
996 adjusted_prop->name = SVN_PROP_MERGEINFO;
997 adjusted_prop->value = filtered_mergeinfo_str;
998 APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *adjusted_prop;
999 }
1000 }
1001 svn_pool_destroy(iterpool);
1002
1003 *props = adjusted_props;
1004 return SVN_NO_ERROR;
1005 }
1006
1007 /* Prepare a set of property changes PROPCHANGES to be used for a merge
1008 operation on LOCAL_ABSPATH.
1009
1010 Remove all non-regular prop-changes (entry-props and WC-props).
1011 Remove all non-mergeinfo prop-changes if it's a record-only merge.
1012 Remove self-referential mergeinfo (### in some cases...)
1013 Remove foreign-repository mergeinfo (### in some cases...)
1014
1015 Store the resulting property changes in *PROP_UPDATES.
1016 Store information on where mergeinfo is updated in MERGE_B.
1017
1018 Used for both file and directory property merges. */
1019 static svn_error_t *
prepare_merge_props_changed(const apr_array_header_t ** prop_updates,const char * local_abspath,const apr_array_header_t * propchanges,merge_cmd_baton_t * merge_b,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1020 prepare_merge_props_changed(const apr_array_header_t **prop_updates,
1021 const char *local_abspath,
1022 const apr_array_header_t *propchanges,
1023 merge_cmd_baton_t *merge_b,
1024 apr_pool_t *result_pool,
1025 apr_pool_t *scratch_pool)
1026 {
1027 apr_array_header_t *props;
1028
1029 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1030
1031 /* We only want to merge "regular" version properties: by
1032 definition, 'svn merge' shouldn't touch any data within .svn/ */
1033 SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
1034 result_pool));
1035
1036 /* If we are only applying mergeinfo changes then we need to do
1037 additional filtering of PROPS so it contains only mergeinfo changes. */
1038 if (merge_b->record_only && props->nelts)
1039 {
1040 apr_array_header_t *mergeinfo_props =
1041 apr_array_make(result_pool, 1, sizeof(svn_prop_t));
1042 int i;
1043
1044 for (i = 0; i < props->nelts; i++)
1045 {
1046 svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
1047
1048 if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
1049 {
1050 APR_ARRAY_PUSH(mergeinfo_props, svn_prop_t) = *prop;
1051 break;
1052 }
1053 }
1054 props = mergeinfo_props;
1055 }
1056
1057 if (props->nelts)
1058 {
1059 /* Issue #3383: We don't want mergeinfo from a foreign repos.
1060
1061 If this is a merge from a foreign repository we must strip all
1062 incoming mergeinfo (including mergeinfo deletions). */
1063 if (! merge_b->same_repos)
1064 SVN_ERR(omit_mergeinfo_changes(&props, props, result_pool));
1065
1066 /* If this is a forward merge then don't add new mergeinfo to
1067 PATH that is already part of PATH's own history, see
1068 http://svn.haxx.se/dev/archive-2008-09/0006.shtml. If the
1069 merge sources are not ancestral then there is no concept of a
1070 'forward' or 'reverse' merge and we filter unconditionally. */
1071 if (merge_b->merge_source.loc1->rev < merge_b->merge_source.loc2->rev
1072 || !merge_b->merge_source.ancestral)
1073 {
1074 if (HONOR_MERGEINFO(merge_b) || merge_b->reintegrate_merge)
1075 SVN_ERR(filter_self_referential_mergeinfo(&props,
1076 local_abspath,
1077 merge_b->ra_session2,
1078 merge_b->ctx,
1079 result_pool));
1080 }
1081 }
1082 *prop_updates = props;
1083
1084 /* Make a record in BATON if we find a PATH where mergeinfo is added
1085 where none existed previously or PATH is having its existing
1086 mergeinfo deleted. */
1087 if (props->nelts)
1088 {
1089 int i;
1090
1091 for (i = 0; i < props->nelts; ++i)
1092 {
1093 svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
1094
1095 if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
1096 {
1097 /* Does LOCAL_ABSPATH have any pristine mergeinfo? */
1098 svn_boolean_t has_pristine_mergeinfo = FALSE;
1099 apr_hash_t *pristine_props;
1100
1101 SVN_ERR(svn_wc_get_pristine_props(&pristine_props,
1102 merge_b->ctx->wc_ctx,
1103 local_abspath,
1104 scratch_pool,
1105 scratch_pool));
1106
1107 if (pristine_props
1108 && svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
1109 has_pristine_mergeinfo = TRUE;
1110
1111 if (!has_pristine_mergeinfo && prop->value)
1112 {
1113 alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
1114 local_abspath, merge_b->pool);
1115 }
1116 else if (has_pristine_mergeinfo && !prop->value)
1117 {
1118 alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
1119 local_abspath, merge_b->pool);
1120 }
1121 }
1122 }
1123 }
1124
1125 return SVN_NO_ERROR;
1126 }
1127
1128 #define CONFLICT_REASON_NONE ((svn_wc_conflict_reason_t)-1)
1129 #define CONFLICT_REASON_SKIP ((svn_wc_conflict_reason_t)-2)
1130 #define CONFLICT_REASON_SKIP_WC ((svn_wc_conflict_reason_t)-3)
1131
1132 /* Baton used for testing trees for being editted while performing tree
1133 conflict detection for incoming deletes */
1134 struct dir_delete_baton_t
1135 {
1136 /* Reference to dir baton of directory that is the root of the deletion */
1137 struct merge_dir_baton_t *del_root;
1138
1139 /* Boolean indicating that some edit is found. Allows avoiding more work */
1140 svn_boolean_t found_edit;
1141
1142 /* A list of paths that are compared. Kept up to date until FOUND_EDIT is
1143 set to TRUE */
1144 apr_hash_t *compared_abspaths;
1145 };
1146
1147 /* Baton for the merge_dir_*() functions. Initialized in merge_dir_opened() */
1148 struct merge_dir_baton_t
1149 {
1150 /* Reference to the parent baton, unless the parent is the anchor, in which
1151 case PARENT_BATON is NULL */
1152 struct merge_dir_baton_t *parent_baton;
1153
1154 /* The pool containing this baton. Use for RESULT_POOL for storing in this
1155 baton */
1156 apr_pool_t *pool;
1157
1158 /* This directory doesn't have a representation in the working copy, so any
1159 operation on it will be skipped and possibly cause a tree conflict on the
1160 shadow root */
1161 svn_boolean_t shadowed;
1162
1163 /* This node or one of its descendants received operational changes from the
1164 merge. If this node is the shadow root its tree conflict status has been
1165 applied */
1166 svn_boolean_t edited;
1167
1168 /* If a tree conflict will be installed once edited, it's reason. If a skip
1169 should be produced its reason. Otherwise CONFLICT_REASON_NONE for no tree
1170 conflict.
1171
1172 Special values:
1173 CONFLICT_REASON_SKIP:
1174 The node will be skipped with content and property state as stored in
1175 SKIP_REASON.
1176
1177 CONFLICT_REASON_SKIP_WC:
1178 The node will be skipped as an obstructing working copy.
1179 */
1180 svn_wc_conflict_reason_t tree_conflict_reason;
1181 svn_wc_conflict_action_t tree_conflict_action;
1182 svn_node_kind_t tree_conflict_local_node_kind;
1183 svn_node_kind_t tree_conflict_merge_left_node_kind;
1184 svn_node_kind_t tree_conflict_merge_right_node_kind;
1185
1186 /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
1187 add to the notification */
1188 svn_wc_notify_state_t skip_reason;
1189
1190 /* TRUE if the node was added by this merge. Otherwise FALSE */
1191 svn_boolean_t added;
1192 svn_boolean_t add_is_replace; /* Add is second part of replace */
1193
1194 /* TRUE if we are taking over an existing directory as addition, otherwise
1195 FALSE. */
1196 svn_boolean_t add_existing;
1197
1198 /* NULL, or an hashtable mapping const char * local_abspaths to
1199 const char *kind mapping, containing deleted nodes that still need a delete
1200 notification (which may be a replaced notification if the node is not just
1201 deleted) */
1202 apr_hash_t *pending_deletes;
1203
1204 /* NULL, or an hashtable mapping const char * LOCAL_ABSPATHs to
1205 a const svn_wc_conflict_description2_t * instance, describing the just
1206 installed conflict */
1207 apr_hash_t *new_tree_conflicts;
1208
1209 /* If not NULL, a reference to the information of the delete test that is
1210 currently in progress. Allocated in the root-directory baton, referenced
1211 from all descendants */
1212 struct dir_delete_baton_t *delete_state;
1213 };
1214
1215 /* Baton for the merge_dir_*() functions. Initialized in merge_file_opened() */
1216 struct merge_file_baton_t
1217 {
1218 /* Reference to the parent baton, unless the parent is the anchor, in which
1219 case PARENT_BATON is NULL */
1220 struct merge_dir_baton_t *parent_baton;
1221
1222 /* This file doesn't have a representation in the working copy, so any
1223 operation on it will be skipped and possibly cause a tree conflict
1224 on the shadow root */
1225 svn_boolean_t shadowed;
1226
1227 /* This node received operational changes from the merge. If this node
1228 is the shadow root its tree conflict status has been applied */
1229 svn_boolean_t edited;
1230
1231 /* If a tree conflict will be installed once edited, it's reason. If a skip
1232 should be produced its reason. Some special values are defined. See the
1233 merge_dir_baton_t for an explanation. */
1234 svn_wc_conflict_reason_t tree_conflict_reason;
1235 svn_wc_conflict_action_t tree_conflict_action;
1236 svn_node_kind_t tree_conflict_local_node_kind;
1237 svn_node_kind_t tree_conflict_merge_left_node_kind;
1238 svn_node_kind_t tree_conflict_merge_right_node_kind;
1239
1240 /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
1241 add to the notification */
1242 svn_wc_notify_state_t skip_reason;
1243
1244 /* TRUE if the node was added by this merge. Otherwise FALSE */
1245 svn_boolean_t added;
1246 svn_boolean_t add_is_replace; /* Add is second part of replace */
1247 };
1248
1249 /* Record the skip for future processing and (later) produce the
1250 skip notification */
1251 static svn_error_t *
record_skip(merge_cmd_baton_t * merge_b,const char * local_abspath,svn_node_kind_t kind,svn_wc_notify_action_t action,svn_wc_notify_state_t state,struct merge_dir_baton_t * pdb,apr_pool_t * scratch_pool)1252 record_skip(merge_cmd_baton_t *merge_b,
1253 const char *local_abspath,
1254 svn_node_kind_t kind,
1255 svn_wc_notify_action_t action,
1256 svn_wc_notify_state_t state,
1257 struct merge_dir_baton_t *pdb,
1258 apr_pool_t *scratch_pool)
1259 {
1260 if (merge_b->record_only)
1261 return SVN_NO_ERROR; /* ### Why? - Legacy compatibility */
1262
1263 if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
1264 && !(pdb && pdb->shadowed))
1265 {
1266 store_path(merge_b->skipped_abspaths, local_abspath);
1267 }
1268
1269 if (merge_b->notify_func)
1270 {
1271 svn_wc_notify_t *notify;
1272
1273 notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
1274 notify->kind = kind;
1275 notify->content_state = notify->prop_state = state;
1276
1277 merge_b->notify_func(merge_b->notify_baton, notify,
1278 scratch_pool);
1279 }
1280 return SVN_NO_ERROR;
1281 }
1282
1283 /* Forward declaration */
1284 static svn_client__merge_path_t *
1285 find_nearest_ancestor_with_intersecting_ranges(
1286 svn_revnum_t *start,
1287 svn_revnum_t *end,
1288 const apr_array_header_t *children_with_mergeinfo,
1289 svn_boolean_t path_is_own_ancestor,
1290 const char *local_abspath);
1291
1292 /* Record a tree conflict in the WC, unless this is a dry run or a record-
1293 * only merge, or if a tree conflict is already flagged for the VICTIM_PATH.
1294 * (The latter can happen if a merge-tracking-aware merge is doing multiple
1295 * editor drives because of a gap in the range of eligible revisions.)
1296 *
1297 * The tree conflict, with its victim specified by VICTIM_PATH, is
1298 * assumed to have happened during a merge using merge baton MERGE_B.
1299 *
1300 * ACTION and REASON correspond to the fields
1301 * of the same names in svn_wc_tree_conflict_description_t.
1302 */
1303 static svn_error_t *
record_tree_conflict(merge_cmd_baton_t * merge_b,const char * local_abspath,struct merge_dir_baton_t * parent_baton,svn_node_kind_t local_node_kind,svn_node_kind_t merge_left_node_kind,svn_node_kind_t merge_right_node_kind,svn_wc_conflict_action_t action,svn_wc_conflict_reason_t reason,const svn_wc_conflict_description2_t * existing_conflict,svn_boolean_t notify_tc,apr_pool_t * scratch_pool)1304 record_tree_conflict(merge_cmd_baton_t *merge_b,
1305 const char *local_abspath,
1306 struct merge_dir_baton_t *parent_baton,
1307 svn_node_kind_t local_node_kind,
1308 svn_node_kind_t merge_left_node_kind,
1309 svn_node_kind_t merge_right_node_kind,
1310 svn_wc_conflict_action_t action,
1311 svn_wc_conflict_reason_t reason,
1312 const svn_wc_conflict_description2_t *existing_conflict,
1313 svn_boolean_t notify_tc,
1314 apr_pool_t *scratch_pool)
1315 {
1316 svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
1317
1318 if (merge_b->record_only)
1319 return SVN_NO_ERROR;
1320
1321 if (merge_b->merge_source.ancestral
1322 || merge_b->reintegrate_merge)
1323 {
1324 store_path(merge_b->tree_conflicted_abspaths, local_abspath);
1325 }
1326
1327 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
1328 merge_b->pool);
1329
1330 if (!merge_b->dry_run)
1331 {
1332 svn_wc_conflict_description2_t *conflict;
1333 const svn_wc_conflict_version_t *left;
1334 const svn_wc_conflict_version_t *right;
1335 apr_pool_t *result_pool = parent_baton ? parent_baton->pool
1336 : scratch_pool;
1337
1338 if (reason == svn_wc_conflict_reason_deleted)
1339 {
1340 const char *moved_to_abspath;
1341
1342 SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
1343 wc_ctx, local_abspath,
1344 scratch_pool, scratch_pool));
1345
1346 if (moved_to_abspath)
1347 {
1348 /* Local abspath itself has been moved away. If only a
1349 descendant is moved away, we call the node itself deleted */
1350 reason = svn_wc_conflict_reason_moved_away;
1351 }
1352 }
1353 else if (reason == svn_wc_conflict_reason_added)
1354 {
1355 const char *moved_from_abspath;
1356 SVN_ERR(svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
1357 wc_ctx, local_abspath,
1358 scratch_pool, scratch_pool));
1359 if (moved_from_abspath)
1360 reason = svn_wc_conflict_reason_moved_here;
1361 }
1362
1363 if (HONOR_MERGEINFO(merge_b) && merge_b->merge_source.ancestral)
1364 {
1365 struct merge_source_t *source;
1366 svn_client__pathrev_t *loc1;
1367 svn_client__pathrev_t *loc2;
1368 svn_merge_range_t range =
1369 {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE};
1370
1371 /* We are honoring mergeinfo so do not blindly record
1372 * a conflict describing the merge of
1373 * SOURCE->LOC1->URL@SOURCE->LOC1->REV through
1374 * SOURCE->LOC2->URL@SOURCE->LOC2->REV
1375 * but figure out the actual revision range merged. */
1376 (void)find_nearest_ancestor_with_intersecting_ranges(
1377 &(range.start), &(range.end),
1378 merge_b->children_with_mergeinfo,
1379 action != svn_wc_conflict_action_delete,
1380 local_abspath);
1381 loc1 = svn_client__pathrev_dup(merge_b->merge_source.loc1,
1382 scratch_pool);
1383 loc2 = svn_client__pathrev_dup(merge_b->merge_source.loc2,
1384 scratch_pool);
1385 loc1->rev = range.start;
1386 loc2->rev = range.end;
1387 source = merge_source_create(loc1, loc2,
1388 merge_b->merge_source.ancestral,
1389 scratch_pool);
1390 SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
1391 merge_left_node_kind,
1392 merge_right_node_kind,
1393 source, merge_b->target,
1394 result_pool, scratch_pool));
1395 }
1396 else
1397 SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
1398 merge_left_node_kind,
1399 merge_right_node_kind,
1400 &merge_b->merge_source, merge_b->target,
1401 result_pool, scratch_pool));
1402
1403 /* Fix up delete of file, add of dir replacement (or other way around) */
1404 if (existing_conflict != NULL && existing_conflict->src_left_version)
1405 left = existing_conflict->src_left_version;
1406
1407 conflict = svn_wc_conflict_description_create_tree2(
1408 local_abspath, local_node_kind,
1409 svn_wc_operation_merge,
1410 left, right, result_pool);
1411
1412 conflict->action = action;
1413 conflict->reason = reason;
1414
1415 /* May return SVN_ERR_WC_PATH_UNEXPECTED_STATUS */
1416 if (existing_conflict)
1417 SVN_ERR(svn_wc__del_tree_conflict(wc_ctx, local_abspath,
1418 scratch_pool));
1419
1420 SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
1421 scratch_pool));
1422
1423 if (parent_baton)
1424 {
1425 if (! parent_baton->new_tree_conflicts)
1426 parent_baton->new_tree_conflicts = apr_hash_make(result_pool);
1427
1428 svn_hash_sets(parent_baton->new_tree_conflicts,
1429 apr_pstrdup(result_pool, local_abspath),
1430 conflict);
1431 }
1432
1433 /* ### TODO: Store in parent baton */
1434 }
1435
1436 /* On a replacement we currently get two tree conflicts */
1437 if (merge_b->notify_func && notify_tc)
1438 {
1439 svn_wc_notify_t *notify;
1440
1441 notify = svn_wc_create_notify(local_abspath, svn_wc_notify_tree_conflict,
1442 scratch_pool);
1443 notify->kind = local_node_kind;
1444
1445 merge_b->notify_func(merge_b->notify_baton, notify,
1446 scratch_pool);
1447 }
1448
1449 return SVN_NO_ERROR;
1450 }
1451
1452 /* Record the add for future processing and produce the
1453 update_add notification
1454 */
1455 static svn_error_t *
record_update_add(merge_cmd_baton_t * merge_b,const char * local_abspath,svn_node_kind_t kind,svn_boolean_t notify_replaced,apr_pool_t * scratch_pool)1456 record_update_add(merge_cmd_baton_t *merge_b,
1457 const char *local_abspath,
1458 svn_node_kind_t kind,
1459 svn_boolean_t notify_replaced,
1460 apr_pool_t *scratch_pool)
1461 {
1462 if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
1463 {
1464 store_path(merge_b->merged_abspaths, local_abspath);
1465 }
1466
1467 if (merge_b->notify_func)
1468 {
1469 svn_wc_notify_t *notify;
1470 svn_wc_notify_action_t action = svn_wc_notify_update_add;
1471
1472 if (notify_replaced)
1473 action = svn_wc_notify_update_replace;
1474
1475 notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
1476 notify->kind = kind;
1477
1478 merge_b->notify_func(merge_b->notify_baton, notify,
1479 scratch_pool);
1480 }
1481
1482 return SVN_NO_ERROR;
1483 }
1484
1485 /* Record the update for future processing and produce the
1486 update_update notification */
1487 static svn_error_t *
record_update_update(merge_cmd_baton_t * merge_b,const char * local_abspath,svn_node_kind_t kind,svn_wc_notify_state_t content_state,svn_wc_notify_state_t prop_state,apr_pool_t * scratch_pool)1488 record_update_update(merge_cmd_baton_t *merge_b,
1489 const char *local_abspath,
1490 svn_node_kind_t kind,
1491 svn_wc_notify_state_t content_state,
1492 svn_wc_notify_state_t prop_state,
1493 apr_pool_t *scratch_pool)
1494 {
1495 if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
1496 {
1497 store_path(merge_b->merged_abspaths, local_abspath);
1498 }
1499
1500 if (merge_b->notify_func)
1501 {
1502 svn_wc_notify_t *notify;
1503
1504 notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_update,
1505 scratch_pool);
1506 notify->kind = kind;
1507 notify->content_state = content_state;
1508 notify->prop_state = prop_state;
1509
1510 merge_b->notify_func(merge_b->notify_baton, notify,
1511 scratch_pool);
1512 }
1513
1514 return SVN_NO_ERROR;
1515 }
1516
1517 /* Record the delete for future processing and for (later) producing the
1518 update_delete notification */
1519 static svn_error_t *
record_update_delete(merge_cmd_baton_t * merge_b,struct merge_dir_baton_t * parent_db,const char * local_abspath,svn_node_kind_t kind,apr_pool_t * scratch_pool)1520 record_update_delete(merge_cmd_baton_t *merge_b,
1521 struct merge_dir_baton_t *parent_db,
1522 const char *local_abspath,
1523 svn_node_kind_t kind,
1524 apr_pool_t *scratch_pool)
1525 {
1526 /* Update the lists of merged, skipped, tree-conflicted and added paths. */
1527 if (merge_b->merge_source.ancestral
1528 || merge_b->reintegrate_merge)
1529 {
1530 /* Issue #4166: If a previous merge added NOTIFY_ABSPATH, but we
1531 are now deleting it, then remove it from the list of added
1532 paths. */
1533 svn_hash_sets(merge_b->added_abspaths, local_abspath, NULL);
1534 store_path(merge_b->merged_abspaths, local_abspath);
1535 }
1536
1537 if (parent_db)
1538 {
1539 const char *dup_abspath = apr_pstrdup(parent_db->pool, local_abspath);
1540
1541 if (!parent_db->pending_deletes)
1542 parent_db->pending_deletes = apr_hash_make(parent_db->pool);
1543
1544 svn_hash_sets(parent_db->pending_deletes, dup_abspath,
1545 svn_node_kind_to_word(kind));
1546 }
1547
1548 /* Note in children_with_mergeinfo that all paths in this subtree are
1549 * being deleted, to avoid trying to set mergeinfo on them later. */
1550 if (merge_b->children_with_mergeinfo)
1551 {
1552 int i;
1553
1554 for (i = 0; i < merge_b->children_with_mergeinfo->nelts; i++)
1555 {
1556 svn_client__merge_path_t *child
1557 = APR_ARRAY_IDX(merge_b->children_with_mergeinfo, i,
1558 svn_client__merge_path_t *);
1559
1560 if (svn_dirent_is_ancestor(local_abspath, child->abspath))
1561 {
1562 SVN_ERR(svn_sort__array_delete2(merge_b->children_with_mergeinfo, i--, 1));
1563 }
1564 }
1565 }
1566
1567 return SVN_NO_ERROR;
1568 }
1569
1570 /* Notify the pending 'D'eletes, that were waiting to see if a matching 'A'dd
1571 might make them a 'R'eplace. */
1572 static svn_error_t *
handle_pending_notifications(merge_cmd_baton_t * merge_b,struct merge_dir_baton_t * db,apr_pool_t * scratch_pool)1573 handle_pending_notifications(merge_cmd_baton_t *merge_b,
1574 struct merge_dir_baton_t *db,
1575 apr_pool_t *scratch_pool)
1576 {
1577 if (merge_b->notify_func && db->pending_deletes)
1578 {
1579 apr_hash_index_t *hi;
1580
1581 for (hi = apr_hash_first(scratch_pool, db->pending_deletes);
1582 hi;
1583 hi = apr_hash_next(hi))
1584 {
1585 const char *del_abspath = apr_hash_this_key(hi);
1586 svn_wc_notify_t *notify;
1587
1588 notify = svn_wc_create_notify(del_abspath,
1589 svn_wc_notify_update_delete,
1590 scratch_pool);
1591 notify->kind = svn_node_kind_from_word(
1592 apr_hash_this_val(hi));
1593
1594 merge_b->notify_func(merge_b->notify_baton,
1595 notify, scratch_pool);
1596 }
1597
1598 db->pending_deletes = NULL;
1599 }
1600 return SVN_NO_ERROR;
1601 }
1602
1603 /* Helper function for the merge_dir_*() and merge_file_*() functions.
1604
1605 Installs and notifies pre-recorded tree conflicts and skips for
1606 ancestors of operational merges
1607 */
1608 static svn_error_t *
mark_dir_edited(merge_cmd_baton_t * merge_b,struct merge_dir_baton_t * db,const char * local_abspath,apr_pool_t * scratch_pool)1609 mark_dir_edited(merge_cmd_baton_t *merge_b,
1610 struct merge_dir_baton_t *db,
1611 const char *local_abspath,
1612 apr_pool_t *scratch_pool)
1613 {
1614 /* ### Too much common code with mark_file_edited */
1615 if (db->edited)
1616 return SVN_NO_ERROR;
1617
1618 if (db->parent_baton && !db->parent_baton->edited)
1619 {
1620 const char *dir_abspath = svn_dirent_dirname(local_abspath,
1621 scratch_pool);
1622
1623 SVN_ERR(mark_dir_edited(merge_b, db->parent_baton, dir_abspath,
1624 scratch_pool));
1625 }
1626
1627 db->edited = TRUE;
1628
1629 if (! db->shadowed)
1630 return SVN_NO_ERROR; /* Easy out */
1631
1632 if (db->parent_baton
1633 && db->parent_baton->delete_state
1634 && db->tree_conflict_reason != CONFLICT_REASON_NONE)
1635 {
1636 db->parent_baton->delete_state->found_edit = TRUE;
1637 }
1638 else if (db->tree_conflict_reason == CONFLICT_REASON_SKIP
1639 || db->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
1640 {
1641 /* open_directory() decided not to flag a tree conflict, but
1642 for clarity we produce a skip for this node that
1643 most likely isn't touched by the merge itself */
1644
1645 if (merge_b->notify_func)
1646 {
1647 svn_wc_notify_t *notify;
1648
1649 notify = svn_wc_create_notify(
1650 local_abspath,
1651 (db->tree_conflict_reason == CONFLICT_REASON_SKIP)
1652 ? svn_wc_notify_skip
1653 : svn_wc_notify_update_skip_obstruction,
1654 scratch_pool);
1655 notify->kind = svn_node_dir;
1656 notify->content_state = notify->prop_state = db->skip_reason;
1657
1658 merge_b->notify_func(merge_b->notify_baton,
1659 notify,
1660 scratch_pool);
1661 }
1662
1663 if (merge_b->merge_source.ancestral
1664 || merge_b->reintegrate_merge)
1665 {
1666 store_path(merge_b->skipped_abspaths, local_abspath);
1667 }
1668 }
1669 else if (db->tree_conflict_reason != CONFLICT_REASON_NONE)
1670 {
1671 /* open_directory() decided that a tree conflict should be raised */
1672
1673 SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
1674 db->tree_conflict_local_node_kind,
1675 db->tree_conflict_merge_left_node_kind,
1676 db->tree_conflict_merge_right_node_kind,
1677 db->tree_conflict_action,
1678 db->tree_conflict_reason,
1679 NULL, TRUE,
1680 scratch_pool));
1681 }
1682
1683 return SVN_NO_ERROR;
1684 }
1685
1686 /* Helper function for the merge_file_*() functions.
1687
1688 Installs and notifies pre-recorded tree conflicts and skips for
1689 ancestors of operational merges
1690 */
1691 static svn_error_t *
mark_file_edited(merge_cmd_baton_t * merge_b,struct merge_file_baton_t * fb,const char * local_abspath,apr_pool_t * scratch_pool)1692 mark_file_edited(merge_cmd_baton_t *merge_b,
1693 struct merge_file_baton_t *fb,
1694 const char *local_abspath,
1695 apr_pool_t *scratch_pool)
1696 {
1697 /* ### Too much common code with mark_dir_edited */
1698 if (fb->edited)
1699 return SVN_NO_ERROR;
1700
1701 if (fb->parent_baton && !fb->parent_baton->edited)
1702 {
1703 const char *dir_abspath = svn_dirent_dirname(local_abspath,
1704 scratch_pool);
1705
1706 SVN_ERR(mark_dir_edited(merge_b, fb->parent_baton, dir_abspath,
1707 scratch_pool));
1708 }
1709
1710 fb->edited = TRUE;
1711
1712 if (! fb->shadowed)
1713 return SVN_NO_ERROR; /* Easy out */
1714
1715 if (fb->parent_baton
1716 && fb->parent_baton->delete_state
1717 && fb->tree_conflict_reason != CONFLICT_REASON_NONE)
1718 {
1719 fb->parent_baton->delete_state->found_edit = TRUE;
1720 }
1721 else if (fb->tree_conflict_reason == CONFLICT_REASON_SKIP
1722 || fb->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
1723 {
1724 /* open_directory() decided not to flag a tree conflict, but
1725 for clarity we produce a skip for this node that
1726 most likely isn't touched by the merge itself */
1727
1728 if (merge_b->notify_func)
1729 {
1730 svn_wc_notify_t *notify;
1731
1732 notify = svn_wc_create_notify(local_abspath, svn_wc_notify_skip,
1733 scratch_pool);
1734 notify->kind = svn_node_file;
1735 notify->content_state = notify->prop_state = fb->skip_reason;
1736
1737 merge_b->notify_func(merge_b->notify_baton,
1738 notify,
1739 scratch_pool);
1740 }
1741
1742 if (merge_b->merge_source.ancestral
1743 || merge_b->reintegrate_merge)
1744 {
1745 store_path(merge_b->skipped_abspaths, local_abspath);
1746 }
1747 }
1748 else if (fb->tree_conflict_reason != CONFLICT_REASON_NONE)
1749 {
1750 /* open_file() decided that a tree conflict should be raised */
1751
1752 SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
1753 fb->tree_conflict_local_node_kind,
1754 fb->tree_conflict_merge_left_node_kind,
1755 fb->tree_conflict_merge_right_node_kind,
1756 fb->tree_conflict_action,
1757 fb->tree_conflict_reason,
1758 NULL, TRUE,
1759 scratch_pool));
1760 }
1761
1762 return SVN_NO_ERROR;
1763 }
1764
1765 /* An svn_diff_tree_processor_t function.
1766
1767 Called before either merge_file_changed(), merge_file_added(),
1768 merge_file_deleted() or merge_file_closed(), unless it sets *SKIP to TRUE.
1769
1770 When *SKIP is TRUE, the diff driver avoids work on getting the details
1771 for the closing callbacks.
1772 */
1773 static svn_error_t *
merge_file_opened(void ** new_file_baton,svn_boolean_t * skip,const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,const svn_diff_source_t * copyfrom_source,void * dir_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1774 merge_file_opened(void **new_file_baton,
1775 svn_boolean_t *skip,
1776 const char *relpath,
1777 const svn_diff_source_t *left_source,
1778 const svn_diff_source_t *right_source,
1779 const svn_diff_source_t *copyfrom_source,
1780 void *dir_baton,
1781 const struct svn_diff_tree_processor_t *processor,
1782 apr_pool_t *result_pool,
1783 apr_pool_t *scratch_pool)
1784 {
1785 merge_cmd_baton_t *merge_b = processor->baton;
1786 struct merge_dir_baton_t *pdb = dir_baton;
1787 struct merge_file_baton_t *fb;
1788 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
1789 relpath, scratch_pool);
1790
1791 fb = apr_pcalloc(result_pool, sizeof(*fb));
1792 fb->tree_conflict_reason = CONFLICT_REASON_NONE;
1793 fb->tree_conflict_action = svn_wc_conflict_action_edit;
1794 fb->skip_reason = svn_wc_notify_state_unknown;
1795
1796 if (left_source)
1797 fb->tree_conflict_merge_left_node_kind = svn_node_file;
1798 else
1799 fb->tree_conflict_merge_left_node_kind = svn_node_none;
1800
1801 if (right_source)
1802 fb->tree_conflict_merge_right_node_kind = svn_node_file;
1803 else
1804 fb->tree_conflict_merge_right_node_kind = svn_node_none;
1805
1806 *new_file_baton = fb;
1807
1808 if (pdb)
1809 {
1810 fb->parent_baton = pdb;
1811 fb->shadowed = pdb->shadowed;
1812 fb->skip_reason = pdb->skip_reason;
1813 }
1814
1815 if (fb->shadowed)
1816 {
1817 /* An ancestor is tree conflicted. Nothing to do here. */
1818 }
1819 else if (left_source != NULL)
1820 {
1821 /* Node is expected to be a file, which will be changed or deleted. */
1822 svn_boolean_t is_deleted;
1823 svn_boolean_t excluded;
1824 svn_depth_t parent_depth;
1825
1826 if (! right_source)
1827 fb->tree_conflict_action = svn_wc_conflict_action_delete;
1828
1829 {
1830 svn_wc_notify_state_t obstr_state;
1831
1832 SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
1833 &fb->tree_conflict_local_node_kind,
1834 &parent_depth,
1835 merge_b, local_abspath,
1836 scratch_pool));
1837
1838 if (obstr_state != svn_wc_notify_state_inapplicable)
1839 {
1840 fb->shadowed = TRUE;
1841 fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1842 fb->skip_reason = obstr_state;
1843 return SVN_NO_ERROR;
1844 }
1845
1846 if (is_deleted)
1847 fb->tree_conflict_local_node_kind = svn_node_none;
1848 }
1849
1850 if (fb->tree_conflict_local_node_kind == svn_node_none)
1851 {
1852 fb->shadowed = TRUE;
1853
1854 /* If this is not the merge target and the parent is too shallow to
1855 contain this directory, and the directory is not present
1856 via exclusion or depth filtering, skip it instead of recording
1857 a tree conflict.
1858
1859 Non-inheritable mergeinfo will be recorded, allowing
1860 future merges into non-shallow working copies to merge
1861 changes we missed this time around. */
1862 if (pdb && (excluded
1863 || (parent_depth != svn_depth_unknown &&
1864 parent_depth < svn_depth_files)))
1865 {
1866 fb->shadowed = TRUE;
1867
1868 fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1869 fb->skip_reason = svn_wc_notify_state_missing;
1870 return SVN_NO_ERROR;
1871 }
1872
1873 if (is_deleted)
1874 fb->tree_conflict_reason = svn_wc_conflict_reason_deleted;
1875 else
1876 fb->tree_conflict_reason = svn_wc_conflict_reason_missing;
1877
1878 /* ### Similar to directory */
1879 *skip = TRUE;
1880 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1881 return SVN_NO_ERROR;
1882 /* ### /Similar */
1883 }
1884 else if (fb->tree_conflict_local_node_kind != svn_node_file)
1885 {
1886 svn_boolean_t added;
1887 fb->shadowed = TRUE;
1888
1889 SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
1890 local_abspath, scratch_pool));
1891
1892 fb->tree_conflict_reason = added ? svn_wc_conflict_reason_added
1893 : svn_wc_conflict_reason_obstructed;
1894
1895 /* ### Similar to directory */
1896 *skip = TRUE;
1897 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1898 return SVN_NO_ERROR;
1899 /* ### /Similar */
1900 }
1901
1902 if (! right_source)
1903 {
1904 /* We want to delete the directory */
1905 fb->tree_conflict_action = svn_wc_conflict_action_delete;
1906 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1907
1908 if (fb->shadowed)
1909 {
1910 return SVN_NO_ERROR; /* Already set a tree conflict */
1911 }
1912
1913 /* Comparison mode to verify for delete tree conflicts? */
1914 if (pdb && pdb->delete_state
1915 && pdb->delete_state->found_edit)
1916 {
1917 /* Earlier nodes found a conflict. Done. */
1918 *skip = TRUE;
1919 }
1920 }
1921 }
1922 else
1923 {
1924 const svn_wc_conflict_description2_t *old_tc = NULL;
1925
1926 /* The node doesn't exist pre-merge: We have an addition */
1927 fb->added = TRUE;
1928 fb->tree_conflict_action = svn_wc_conflict_action_add;
1929
1930 if (pdb && pdb->pending_deletes
1931 && svn_hash_gets(pdb->pending_deletes, local_abspath))
1932 {
1933 fb->add_is_replace = TRUE;
1934 fb->tree_conflict_action = svn_wc_conflict_action_replace;
1935
1936 svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
1937 }
1938
1939 if (pdb
1940 && pdb->new_tree_conflicts
1941 && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
1942 {
1943 fb->tree_conflict_action = svn_wc_conflict_action_replace;
1944 fb->tree_conflict_reason = old_tc->reason;
1945
1946 /* Update the tree conflict to store that this is a replace */
1947 SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
1948 old_tc->node_kind,
1949 old_tc->src_left_version->node_kind,
1950 svn_node_file,
1951 fb->tree_conflict_action,
1952 fb->tree_conflict_reason,
1953 old_tc, FALSE,
1954 scratch_pool));
1955
1956 if (old_tc->reason == svn_wc_conflict_reason_deleted
1957 || old_tc->reason == svn_wc_conflict_reason_moved_away)
1958 {
1959 /* Issue #3806: Incoming replacements on local deletes produce
1960 inconsistent result.
1961
1962 In this specific case we can continue applying the add part
1963 of the replacement. */
1964 }
1965 else
1966 {
1967 *skip = TRUE;
1968
1969 return SVN_NO_ERROR;
1970 }
1971 }
1972 else if (! (merge_b->dry_run
1973 && ((pdb && pdb->added) || fb->add_is_replace)))
1974 {
1975 svn_wc_notify_state_t obstr_state;
1976 svn_boolean_t is_deleted;
1977
1978 SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
1979 &fb->tree_conflict_local_node_kind,
1980 NULL, merge_b, local_abspath,
1981 scratch_pool));
1982
1983 if (obstr_state != svn_wc_notify_state_inapplicable)
1984 {
1985 /* Skip the obstruction */
1986 fb->shadowed = TRUE;
1987 fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1988 fb->skip_reason = obstr_state;
1989 }
1990 else if (fb->tree_conflict_local_node_kind != svn_node_none
1991 && !is_deleted)
1992 {
1993 /* Set a tree conflict */
1994 svn_boolean_t added;
1995
1996 fb->shadowed = TRUE;
1997 SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
1998 local_abspath, scratch_pool));
1999
2000 fb->tree_conflict_reason = added ? svn_wc_conflict_reason_added
2001 : svn_wc_conflict_reason_obstructed;
2002 }
2003 }
2004
2005 /* Handle pending conflicts */
2006 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2007 }
2008
2009 return SVN_NO_ERROR;
2010 }
2011
2012 /* An svn_diff_tree_processor_t function.
2013 *
2014 * Called after merge_file_opened() when a node receives only text and/or
2015 * property changes between LEFT_SOURCE and RIGHT_SOURCE.
2016 *
2017 * left_file and right_file can be NULL when the file is not modified.
2018 * left_props and right_props are always available.
2019 */
2020 static svn_error_t *
merge_file_changed(const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,const char * left_file,const char * right_file,apr_hash_t * left_props,apr_hash_t * right_props,svn_boolean_t file_modified,const apr_array_header_t * prop_changes,void * file_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2021 merge_file_changed(const char *relpath,
2022 const svn_diff_source_t *left_source,
2023 const svn_diff_source_t *right_source,
2024 const char *left_file,
2025 const char *right_file,
2026 /*const*/ apr_hash_t *left_props,
2027 /*const*/ apr_hash_t *right_props,
2028 svn_boolean_t file_modified,
2029 const apr_array_header_t *prop_changes,
2030 void *file_baton,
2031 const struct svn_diff_tree_processor_t *processor,
2032 apr_pool_t *scratch_pool)
2033 {
2034 merge_cmd_baton_t *merge_b = processor->baton;
2035 struct merge_file_baton_t *fb = file_baton;
2036 svn_client_ctx_t *ctx = merge_b->ctx;
2037 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2038 relpath, scratch_pool);
2039 const svn_wc_conflict_version_t *left;
2040 const svn_wc_conflict_version_t *right;
2041 svn_wc_notify_state_t text_state;
2042 svn_wc_notify_state_t property_state;
2043
2044 SVN_ERR_ASSERT(local_abspath && svn_dirent_is_absolute(local_abspath));
2045 SVN_ERR_ASSERT(!left_file || svn_dirent_is_absolute(left_file));
2046 SVN_ERR_ASSERT(!right_file || svn_dirent_is_absolute(right_file));
2047
2048 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2049
2050 if (fb->shadowed)
2051 {
2052 if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
2053 {
2054 /* We haven't notified for this node yet: report a skip */
2055 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
2056 svn_wc_notify_update_shadowed_update,
2057 fb->skip_reason, fb->parent_baton,
2058 scratch_pool));
2059 }
2060
2061 return SVN_NO_ERROR;
2062 }
2063
2064 /* This callback is essentially no more than a wrapper around
2065 svn_wc_merge5(). Thank goodness that all the
2066 diff-editor-mechanisms are doing the hard work of getting the
2067 fulltexts! */
2068
2069 property_state = svn_wc_notify_state_unchanged;
2070 text_state = svn_wc_notify_state_unchanged;
2071
2072 SVN_ERR(prepare_merge_props_changed(&prop_changes, local_abspath,
2073 prop_changes, merge_b,
2074 scratch_pool, scratch_pool));
2075
2076 SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
2077 svn_node_file, svn_node_file,
2078 &merge_b->merge_source, merge_b->target,
2079 scratch_pool, scratch_pool));
2080
2081 /* Do property merge now, if we are not going to perform a text merge */
2082 if ((merge_b->record_only || !left_file) && prop_changes->nelts)
2083 {
2084 SVN_ERR(svn_wc_merge_props3(&property_state, ctx->wc_ctx, local_abspath,
2085 left, right,
2086 left_props, prop_changes,
2087 merge_b->dry_run,
2088 NULL, NULL,
2089 ctx->cancel_func, ctx->cancel_baton,
2090 scratch_pool));
2091 if (property_state == svn_wc_notify_state_conflicted)
2092 {
2093 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
2094 merge_b->pool);
2095 }
2096 }
2097
2098 /* Easy out: We are only applying mergeinfo differences. */
2099 if (merge_b->record_only)
2100 {
2101 /* NO-OP */
2102 }
2103 else if (left_file)
2104 {
2105 svn_boolean_t has_local_mods;
2106 enum svn_wc_merge_outcome_t content_outcome;
2107 const char *target_label;
2108 const char *left_label;
2109 const char *right_label;
2110 const char *path_ext = "";
2111
2112 if (merge_b->ext_patterns && merge_b->ext_patterns->nelts)
2113 {
2114 svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
2115 if (! (*path_ext
2116 && svn_cstring_match_glob_list(path_ext,
2117 merge_b->ext_patterns)))
2118 {
2119 path_ext = "";
2120 }
2121 }
2122
2123 /* xgettext: the '.working', '.merge-left.r%ld' and
2124 '.merge-right.r%ld' strings are used to tag onto a file
2125 name in case of a merge conflict */
2126
2127 target_label = apr_psprintf(scratch_pool, _(".working%s%s"),
2128 *path_ext ? "." : "", path_ext);
2129 left_label = apr_psprintf(scratch_pool,
2130 _(".merge-left.r%ld%s%s"),
2131 left_source->revision,
2132 *path_ext ? "." : "", path_ext);
2133 right_label = apr_psprintf(scratch_pool,
2134 _(".merge-right.r%ld%s%s"),
2135 right_source->revision,
2136 *path_ext ? "." : "", path_ext);
2137
2138 SVN_ERR(svn_wc_text_modified_p2(&has_local_mods, ctx->wc_ctx,
2139 local_abspath, FALSE, scratch_pool));
2140
2141 /* Do property merge and text merge in one step so that keyword expansion
2142 takes into account the new property values. */
2143 SVN_ERR(svn_wc_merge5(&content_outcome, &property_state, ctx->wc_ctx,
2144 left_file, right_file, local_abspath,
2145 left_label, right_label, target_label,
2146 left, right,
2147 merge_b->dry_run, merge_b->diff3_cmd,
2148 merge_b->merge_options,
2149 left_props, prop_changes,
2150 NULL, NULL,
2151 ctx->cancel_func,
2152 ctx->cancel_baton,
2153 scratch_pool));
2154
2155 if (content_outcome == svn_wc_merge_conflict
2156 || property_state == svn_wc_notify_state_conflicted)
2157 {
2158 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
2159 merge_b->pool);
2160 }
2161
2162 if (content_outcome == svn_wc_merge_conflict)
2163 text_state = svn_wc_notify_state_conflicted;
2164 else if (has_local_mods
2165 && content_outcome != svn_wc_merge_unchanged)
2166 text_state = svn_wc_notify_state_merged;
2167 else if (content_outcome == svn_wc_merge_merged)
2168 text_state = svn_wc_notify_state_changed;
2169 else if (content_outcome == svn_wc_merge_no_merge)
2170 text_state = svn_wc_notify_state_missing;
2171 else /* merge_outcome == svn_wc_merge_unchanged */
2172 text_state = svn_wc_notify_state_unchanged;
2173 }
2174
2175 if (text_state == svn_wc_notify_state_conflicted
2176 || text_state == svn_wc_notify_state_merged
2177 || text_state == svn_wc_notify_state_changed
2178 || property_state == svn_wc_notify_state_conflicted
2179 || property_state == svn_wc_notify_state_merged
2180 || property_state == svn_wc_notify_state_changed)
2181 {
2182 SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
2183 text_state, property_state,
2184 scratch_pool));
2185 }
2186
2187 return SVN_NO_ERROR;
2188 }
2189
2190 /* An svn_diff_tree_processor_t function.
2191 *
2192 * Called after merge_file_opened() when a node doesn't exist in LEFT_SOURCE,
2193 * but does in RIGHT_SOURCE.
2194 *
2195 * When a node is replaced instead of just added a separate opened+deleted will
2196 * be invoked before the current open+added.
2197 */
2198 static svn_error_t *
merge_file_added(const char * relpath,const svn_diff_source_t * copyfrom_source,const svn_diff_source_t * right_source,const char * copyfrom_file,const char * right_file,apr_hash_t * copyfrom_props,apr_hash_t * right_props,void * file_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2199 merge_file_added(const char *relpath,
2200 const svn_diff_source_t *copyfrom_source,
2201 const svn_diff_source_t *right_source,
2202 const char *copyfrom_file,
2203 const char *right_file,
2204 /*const*/ apr_hash_t *copyfrom_props,
2205 /*const*/ apr_hash_t *right_props,
2206 void *file_baton,
2207 const struct svn_diff_tree_processor_t *processor,
2208 apr_pool_t *scratch_pool)
2209 {
2210 merge_cmd_baton_t *merge_b = processor->baton;
2211 struct merge_file_baton_t *fb = file_baton;
2212 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2213 relpath, scratch_pool);
2214 apr_hash_t *pristine_props;
2215 apr_hash_t *new_props;
2216
2217 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2218
2219 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2220
2221 if (fb->shadowed)
2222 {
2223 if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
2224 {
2225 /* We haven't notified for this node yet: report a skip */
2226 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
2227 svn_wc_notify_update_shadowed_add,
2228 fb->skip_reason, fb->parent_baton,
2229 scratch_pool));
2230 }
2231
2232 return SVN_NO_ERROR;
2233 }
2234
2235 /* Easy out: We are only applying mergeinfo differences. */
2236 if (merge_b->record_only)
2237 {
2238 return SVN_NO_ERROR;
2239 }
2240
2241 if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
2242 && ( !fb->parent_baton || !fb->parent_baton->added))
2243 {
2244 /* Store the roots of added subtrees */
2245 store_path(merge_b->added_abspaths, local_abspath);
2246 }
2247
2248 if (!merge_b->dry_run)
2249 {
2250 const char *copyfrom_url;
2251 svn_revnum_t copyfrom_rev;
2252 svn_stream_t *new_contents, *pristine_contents;
2253
2254 /* If this is a merge from the same repository as our
2255 working copy, we handle adds as add-with-history.
2256 Otherwise, we'll use a pure add. */
2257 if (merge_b->same_repos)
2258 {
2259 copyfrom_url = svn_path_url_add_component2(
2260 merge_b->merge_source.loc2->url,
2261 relpath, scratch_pool);
2262 copyfrom_rev = right_source->revision;
2263 SVN_ERR(check_repos_match(merge_b->target, local_abspath,
2264 copyfrom_url, scratch_pool));
2265 SVN_ERR(svn_stream_open_readonly(&pristine_contents,
2266 right_file,
2267 scratch_pool,
2268 scratch_pool));
2269 new_contents = NULL; /* inherit from new_base_contents */
2270
2271 pristine_props = right_props; /* Includes last_* information */
2272 new_props = NULL; /* No local changes */
2273
2274 if (svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
2275 {
2276 alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
2277 local_abspath, merge_b->pool);
2278 }
2279 }
2280 else
2281 {
2282 apr_array_header_t *regular_props;
2283
2284 copyfrom_url = NULL;
2285 copyfrom_rev = SVN_INVALID_REVNUM;
2286
2287 pristine_contents = svn_stream_empty(scratch_pool);
2288 SVN_ERR(svn_stream_open_readonly(&new_contents, right_file,
2289 scratch_pool, scratch_pool));
2290
2291 pristine_props = apr_hash_make(scratch_pool); /* Local addition */
2292
2293 /* We don't want any foreign properties */
2294 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props,
2295 scratch_pool),
2296 NULL, NULL, ®ular_props,
2297 scratch_pool));
2298
2299 new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
2300
2301 /* Issue #3383: We don't want mergeinfo from a foreign repository. */
2302 svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
2303 }
2304
2305 /* Do everything like if we had called 'svn cp PATH1 PATH2'. */
2306 SVN_ERR(svn_wc_add_repos_file4(merge_b->ctx->wc_ctx,
2307 local_abspath,
2308 pristine_contents,
2309 new_contents,
2310 pristine_props, new_props,
2311 copyfrom_url, copyfrom_rev,
2312 merge_b->ctx->cancel_func,
2313 merge_b->ctx->cancel_baton,
2314 scratch_pool));
2315
2316 /* Caller must call svn_sleep_for_timestamps() */
2317 *merge_b->use_sleep = TRUE;
2318 }
2319
2320 SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_file,
2321 fb->add_is_replace, scratch_pool));
2322
2323 return SVN_NO_ERROR;
2324 }
2325
2326 /* Compare the two sets of properties PROPS1 and PROPS2, ignoring the
2327 * "svn:mergeinfo" property, and noticing only "normal" props. Set *SAME to
2328 * true if the rest of the properties are identical or false if they differ.
2329 */
2330 static svn_error_t *
properties_same_p(svn_boolean_t * same,apr_hash_t * props1,apr_hash_t * props2,apr_pool_t * scratch_pool)2331 properties_same_p(svn_boolean_t *same,
2332 apr_hash_t *props1,
2333 apr_hash_t *props2,
2334 apr_pool_t *scratch_pool)
2335 {
2336 apr_array_header_t *prop_changes;
2337 int i, diffs;
2338
2339 /* Examine the properties that differ */
2340 SVN_ERR(svn_prop_diffs(&prop_changes, props1, props2, scratch_pool));
2341 diffs = 0;
2342 for (i = 0; i < prop_changes->nelts; i++)
2343 {
2344 const char *pname = APR_ARRAY_IDX(prop_changes, i, svn_prop_t).name;
2345
2346 /* Count the properties we're interested in; ignore the rest */
2347 if (svn_wc_is_normal_prop(pname)
2348 && strcmp(pname, SVN_PROP_MERGEINFO) != 0)
2349 diffs++;
2350 }
2351 *same = (diffs == 0);
2352 return SVN_NO_ERROR;
2353 }
2354
2355 /* Compare the file OLDER_ABSPATH (together with its normal properties in
2356 * ORIGINAL_PROPS which may also contain WC props and entry props) with the
2357 * versioned file MINE_ABSPATH (together with its versioned properties).
2358 * Set *SAME to true if they are the same or false if they differ, ignoring
2359 * the "svn:mergeinfo" property, and ignoring differences in keyword
2360 * expansion and end-of-line style. */
2361 static svn_error_t *
files_same_p(svn_boolean_t * same,const char * older_abspath,apr_hash_t * original_props,const char * mine_abspath,svn_wc_context_t * wc_ctx,apr_pool_t * scratch_pool)2362 files_same_p(svn_boolean_t *same,
2363 const char *older_abspath,
2364 apr_hash_t *original_props,
2365 const char *mine_abspath,
2366 svn_wc_context_t *wc_ctx,
2367 apr_pool_t *scratch_pool)
2368 {
2369 apr_hash_t *working_props;
2370
2371 SVN_ERR(svn_wc_prop_list2(&working_props, wc_ctx, mine_abspath,
2372 scratch_pool, scratch_pool));
2373
2374 /* Compare the properties */
2375 SVN_ERR(properties_same_p(same, original_props, working_props,
2376 scratch_pool));
2377 if (*same)
2378 {
2379 svn_stream_t *mine_stream;
2380 svn_stream_t *older_stream;
2381 svn_string_t *special = svn_hash_gets(working_props, SVN_PROP_SPECIAL);
2382 svn_string_t *eol_style = svn_hash_gets(working_props, SVN_PROP_EOL_STYLE);
2383 svn_string_t *keywords = svn_hash_gets(working_props, SVN_PROP_KEYWORDS);
2384
2385 /* Compare the file content, translating 'mine' to 'normal' form. */
2386 if (special != NULL)
2387 SVN_ERR(svn_subst_read_specialfile(&mine_stream, mine_abspath,
2388 scratch_pool, scratch_pool));
2389 else
2390 SVN_ERR(svn_stream_open_readonly(&mine_stream, mine_abspath,
2391 scratch_pool, scratch_pool));
2392
2393 if (!special && (eol_style || keywords))
2394 {
2395 apr_hash_t *kw = NULL;
2396 const char *eol = NULL;
2397 svn_subst_eol_style_t style;
2398
2399 /* We used to use svn_client__get_normalized_stream() here, but
2400 that doesn't work in 100% of the cases because it doesn't
2401 convert EOLs to the repository form; just to '\n'.
2402 */
2403
2404 if (eol_style)
2405 {
2406 svn_subst_eol_style_from_value(&style, &eol, eol_style->data);
2407
2408 if (style == svn_subst_eol_style_native)
2409 eol = SVN_SUBST_NATIVE_EOL_STR;
2410 else if (style != svn_subst_eol_style_fixed
2411 && style != svn_subst_eol_style_none)
2412 return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL);
2413 }
2414
2415 if (keywords)
2416 SVN_ERR(svn_subst_build_keywords3(&kw, keywords->data, "", "",
2417 "", 0, "", scratch_pool));
2418
2419 mine_stream = svn_subst_stream_translated(
2420 mine_stream, eol, FALSE, kw, FALSE, scratch_pool);
2421 }
2422
2423 SVN_ERR(svn_stream_open_readonly(&older_stream, older_abspath,
2424 scratch_pool, scratch_pool));
2425
2426 SVN_ERR(svn_stream_contents_same2(same, mine_stream, older_stream,
2427 scratch_pool));
2428
2429 }
2430
2431 return SVN_NO_ERROR;
2432 }
2433
2434 /* An svn_diff_tree_processor_t function.
2435 *
2436 * Called after merge_file_opened() when a node does exist in LEFT_SOURCE, but
2437 * no longer exists (or is replaced) in RIGHT_SOURCE.
2438 *
2439 * When a node is replaced instead of just added a separate opened+added will
2440 * be invoked after the current open+deleted.
2441 */
2442 static svn_error_t *
merge_file_deleted(const char * relpath,const svn_diff_source_t * left_source,const char * left_file,apr_hash_t * left_props,void * file_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2443 merge_file_deleted(const char *relpath,
2444 const svn_diff_source_t *left_source,
2445 const char *left_file,
2446 /*const*/ apr_hash_t *left_props,
2447 void *file_baton,
2448 const struct svn_diff_tree_processor_t *processor,
2449 apr_pool_t *scratch_pool)
2450 {
2451 merge_cmd_baton_t *merge_b = processor->baton;
2452 struct merge_file_baton_t *fb = file_baton;
2453 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2454 relpath, scratch_pool);
2455 svn_boolean_t same;
2456
2457 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2458
2459 if (fb->shadowed)
2460 {
2461 if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
2462 {
2463 /* We haven't notified for this node yet: report a skip */
2464 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
2465 svn_wc_notify_update_shadowed_delete,
2466 fb->skip_reason, fb->parent_baton,
2467 scratch_pool));
2468 }
2469
2470 return SVN_NO_ERROR;
2471 }
2472
2473 /* Easy out: We are only applying mergeinfo differences. */
2474 if (merge_b->record_only)
2475 {
2476 return SVN_NO_ERROR;
2477 }
2478
2479 /* If the files are identical, attempt deletion */
2480 if (merge_b->force_delete)
2481 same = TRUE;
2482 else
2483 SVN_ERR(files_same_p(&same, left_file, left_props,
2484 local_abspath, merge_b->ctx->wc_ctx,
2485 scratch_pool));
2486
2487 if (fb->parent_baton
2488 && fb->parent_baton->delete_state)
2489 {
2490 if (same)
2491 {
2492 /* Note that we checked this file */
2493 store_path(fb->parent_baton->delete_state->compared_abspaths,
2494 local_abspath);
2495 }
2496 else
2497 {
2498 /* We found some modification. Parent should raise a tree conflict */
2499 fb->parent_baton->delete_state->found_edit = TRUE;
2500 }
2501
2502 return SVN_NO_ERROR;
2503 }
2504 else if (same)
2505 {
2506 if (!merge_b->dry_run)
2507 SVN_ERR(svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
2508 FALSE /* keep_local */, FALSE /* unversioned */,
2509 merge_b->ctx->cancel_func,
2510 merge_b->ctx->cancel_baton,
2511 NULL, NULL /* no notify */,
2512 scratch_pool));
2513
2514 /* Record that we might have deleted mergeinfo */
2515 alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
2516 local_abspath, merge_b->pool);
2517
2518 /* And notify the deletion */
2519 SVN_ERR(record_update_delete(merge_b, fb->parent_baton, local_abspath,
2520 svn_node_file, scratch_pool));
2521 }
2522 else
2523 {
2524 /* The files differ, so raise a conflict instead of deleting */
2525
2526 /* This is use case 5 described in the paper attached to issue
2527 * #2282. See also notes/tree-conflicts/detection.txt
2528 */
2529 SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
2530 svn_node_file,
2531 svn_node_file,
2532 svn_node_none,
2533 svn_wc_conflict_action_delete,
2534 svn_wc_conflict_reason_edited,
2535 NULL, TRUE,
2536 scratch_pool));
2537 }
2538
2539 return SVN_NO_ERROR;
2540 }
2541
2542 /* An svn_diff_tree_processor_t function.
2543
2544 Called before either merge_dir_changed(), merge_dir_added(),
2545 merge_dir_deleted() or merge_dir_closed(), unless it sets *SKIP to TRUE.
2546
2547 After this call and before the close call, all descendants will receive
2548 their changes, unless *SKIP_CHILDREN is set to TRUE.
2549
2550 When *SKIP is TRUE, the diff driver avoids work on getting the details
2551 for the closing callbacks.
2552
2553 The SKIP and SKIP_DESCENDANTS work independently.
2554 */
2555 static svn_error_t *
merge_dir_opened(void ** new_dir_baton,svn_boolean_t * skip,svn_boolean_t * skip_children,const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,const svn_diff_source_t * copyfrom_source,void * parent_dir_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2556 merge_dir_opened(void **new_dir_baton,
2557 svn_boolean_t *skip,
2558 svn_boolean_t *skip_children,
2559 const char *relpath,
2560 const svn_diff_source_t *left_source,
2561 const svn_diff_source_t *right_source,
2562 const svn_diff_source_t *copyfrom_source,
2563 void *parent_dir_baton,
2564 const struct svn_diff_tree_processor_t *processor,
2565 apr_pool_t *result_pool,
2566 apr_pool_t *scratch_pool)
2567 {
2568 merge_cmd_baton_t *merge_b = processor->baton;
2569 struct merge_dir_baton_t *db;
2570 struct merge_dir_baton_t *pdb = parent_dir_baton;
2571
2572 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2573 relpath, scratch_pool);
2574
2575 db = apr_pcalloc(result_pool, sizeof(*db));
2576 db->pool = result_pool;
2577 db->tree_conflict_reason = CONFLICT_REASON_NONE;
2578 db->tree_conflict_action = svn_wc_conflict_action_edit;
2579 db->skip_reason = svn_wc_notify_state_unknown;
2580
2581 *new_dir_baton = db;
2582
2583 if (left_source)
2584 db->tree_conflict_merge_left_node_kind = svn_node_dir;
2585 else
2586 db->tree_conflict_merge_left_node_kind = svn_node_none;
2587
2588 if (right_source)
2589 db->tree_conflict_merge_right_node_kind = svn_node_dir;
2590 else
2591 db->tree_conflict_merge_right_node_kind = svn_node_none;
2592
2593 if (pdb)
2594 {
2595 db->parent_baton = pdb;
2596 db->shadowed = pdb->shadowed;
2597 db->skip_reason = pdb->skip_reason;
2598 }
2599
2600 if (db->shadowed)
2601 {
2602 /* An ancestor is tree conflicted. Nothing to do here. */
2603 if (! left_source)
2604 db->added = TRUE;
2605 }
2606 else if (left_source != NULL)
2607 {
2608 /* Node is expected to be a directory. */
2609 svn_boolean_t is_deleted;
2610 svn_boolean_t excluded;
2611 svn_depth_t parent_depth;
2612
2613 if (! right_source)
2614 db->tree_conflict_action = svn_wc_conflict_action_delete;
2615
2616 /* Check for an obstructed or missing node on disk. */
2617 {
2618 svn_wc_notify_state_t obstr_state;
2619 SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
2620 &db->tree_conflict_local_node_kind,
2621 &parent_depth, merge_b,
2622 local_abspath, scratch_pool));
2623
2624 if (obstr_state != svn_wc_notify_state_inapplicable)
2625 {
2626 db->shadowed = TRUE;
2627
2628 if (obstr_state == svn_wc_notify_state_obstructed)
2629 {
2630 svn_boolean_t is_wcroot;
2631
2632 SVN_ERR(svn_wc_check_root(&is_wcroot, NULL, NULL,
2633 merge_b->ctx->wc_ctx,
2634 local_abspath, scratch_pool));
2635
2636 if (is_wcroot)
2637 {
2638 db->tree_conflict_reason = CONFLICT_REASON_SKIP_WC;
2639 return SVN_NO_ERROR;
2640 }
2641 }
2642
2643 db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2644 db->skip_reason = obstr_state;
2645
2646 if (! right_source)
2647 {
2648 *skip = *skip_children = TRUE;
2649 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath,
2650 scratch_pool));
2651 }
2652
2653 return SVN_NO_ERROR;
2654 }
2655
2656 if (is_deleted)
2657 db->tree_conflict_local_node_kind = svn_node_none;
2658 }
2659
2660 if (db->tree_conflict_local_node_kind == svn_node_none)
2661 {
2662 db->shadowed = TRUE;
2663
2664 /* If this is not the merge target and the parent is too shallow to
2665 contain this directory, and the directory is not presen
2666 via exclusion or depth filtering, skip it instead of recording
2667 a tree conflict.
2668
2669 Non-inheritable mergeinfo will be recorded, allowing
2670 future merges into non-shallow working copies to merge
2671 changes we missed this time around. */
2672 if (pdb && (excluded
2673 || (parent_depth != svn_depth_unknown &&
2674 parent_depth < svn_depth_immediates)))
2675 {
2676 db->shadowed = TRUE;
2677
2678 db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2679 db->skip_reason = svn_wc_notify_state_missing;
2680
2681 return SVN_NO_ERROR;
2682 }
2683
2684 if (is_deleted)
2685 db->tree_conflict_reason = svn_wc_conflict_reason_deleted;
2686 else
2687 db->tree_conflict_reason = svn_wc_conflict_reason_missing;
2688
2689 /* ### To avoid breaking tests */
2690 *skip = TRUE;
2691 *skip_children = TRUE;
2692 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2693 return SVN_NO_ERROR;
2694 /* ### /avoid breaking tests */
2695 }
2696 else if (db->tree_conflict_local_node_kind != svn_node_dir)
2697 {
2698 svn_boolean_t added;
2699
2700 db->shadowed = TRUE;
2701 SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
2702 local_abspath, scratch_pool));
2703
2704 db->tree_conflict_reason = added ? svn_wc_conflict_reason_added
2705 : svn_wc_conflict_reason_obstructed;
2706
2707 /* ### To avoid breaking tests */
2708 *skip = TRUE;
2709 *skip_children = TRUE;
2710 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2711 return SVN_NO_ERROR;
2712 /* ### /avoid breaking tests */
2713 }
2714
2715 if (! right_source)
2716 {
2717 /* We want to delete the directory */
2718 /* Mark PB edited now? */
2719 db->tree_conflict_action = svn_wc_conflict_action_delete;
2720 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2721
2722 if (db->shadowed)
2723 {
2724 *skip_children = TRUE;
2725 return SVN_NO_ERROR; /* Already set a tree conflict */
2726 }
2727
2728 db->delete_state = (pdb != NULL) ? pdb->delete_state : NULL;
2729
2730 if (db->delete_state && db->delete_state->found_edit)
2731 {
2732 /* A sibling found a conflict. Done. */
2733 *skip = TRUE;
2734 *skip_children = TRUE;
2735 }
2736 else if (merge_b->force_delete)
2737 {
2738 /* No comparison necessary */
2739 *skip_children = TRUE;
2740 }
2741 else if (! db->delete_state)
2742 {
2743 /* Start descendant comparison */
2744 db->delete_state = apr_pcalloc(db->pool,
2745 sizeof(*db->delete_state));
2746
2747 db->delete_state->del_root = db;
2748 db->delete_state->compared_abspaths = apr_hash_make(db->pool);
2749 }
2750 }
2751 }
2752 else
2753 {
2754 const svn_wc_conflict_description2_t *old_tc = NULL;
2755
2756 /* The node doesn't exist pre-merge: We have an addition */
2757 db->added = TRUE;
2758 db->tree_conflict_action = svn_wc_conflict_action_add;
2759
2760 if (pdb && pdb->pending_deletes
2761 && svn_hash_gets(pdb->pending_deletes, local_abspath))
2762 {
2763 db->add_is_replace = TRUE;
2764 db->tree_conflict_action = svn_wc_conflict_action_replace;
2765
2766 svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
2767 }
2768
2769 if (pdb
2770 && pdb->new_tree_conflicts
2771 && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
2772 {
2773 db->tree_conflict_action = svn_wc_conflict_action_replace;
2774 db->tree_conflict_reason = old_tc->reason;
2775
2776 if (old_tc->reason == svn_wc_conflict_reason_deleted
2777 || old_tc->reason == svn_wc_conflict_reason_moved_away)
2778 {
2779 /* Issue #3806: Incoming replacements on local deletes produce
2780 inconsistent result.
2781
2782 In this specific case we can continue applying the add part
2783 of the replacement. */
2784 }
2785 else
2786 {
2787 *skip = TRUE;
2788 *skip_children = TRUE;
2789
2790 /* Update the tree conflict to store that this is a replace */
2791 SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
2792 old_tc->node_kind,
2793 old_tc->src_left_version->node_kind,
2794 svn_node_dir,
2795 db->tree_conflict_action,
2796 db->tree_conflict_reason,
2797 old_tc, FALSE,
2798 scratch_pool));
2799
2800 return SVN_NO_ERROR;
2801 }
2802 }
2803
2804 if (! (merge_b->dry_run
2805 && ((pdb && pdb->added) || db->add_is_replace)))
2806 {
2807 svn_wc_notify_state_t obstr_state;
2808 svn_boolean_t is_deleted;
2809
2810 SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
2811 &db->tree_conflict_local_node_kind,
2812 NULL, merge_b, local_abspath,
2813 scratch_pool));
2814
2815 /* In this case of adding a directory, we have an exception to the
2816 * usual "skip if it's inconsistent" rule. If the directory exists
2817 * on disk unexpectedly, we simply make it versioned, because we can
2818 * do so without risk of destroying data. Only skip if it is
2819 * versioned but unexpectedly missing from disk, or is unversioned
2820 * but obstructed by a node of the wrong kind. */
2821 if (obstr_state == svn_wc_notify_state_obstructed
2822 && (is_deleted ||
2823 db->tree_conflict_local_node_kind == svn_node_none))
2824 {
2825 svn_node_kind_t disk_kind;
2826
2827 SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
2828 scratch_pool));
2829
2830 if (disk_kind == svn_node_dir)
2831 {
2832 obstr_state = svn_wc_notify_state_inapplicable;
2833 db->add_existing = TRUE; /* Take over existing directory */
2834 }
2835 }
2836
2837 if (obstr_state != svn_wc_notify_state_inapplicable)
2838 {
2839 /* Skip the obstruction */
2840 db->shadowed = TRUE;
2841 db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2842 db->skip_reason = obstr_state;
2843 }
2844 else if (db->tree_conflict_local_node_kind != svn_node_none
2845 && !is_deleted)
2846 {
2847 /* Set a tree conflict */
2848 svn_boolean_t added;
2849 db->shadowed = TRUE;
2850
2851 SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
2852 local_abspath, scratch_pool));
2853
2854 db->tree_conflict_reason = added ? svn_wc_conflict_reason_added
2855 : svn_wc_conflict_reason_obstructed;
2856 }
2857 }
2858
2859 /* Handle pending conflicts */
2860 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2861
2862 if (db->shadowed)
2863 {
2864 /* Notified and done. Skip children? */
2865 }
2866 else if (merge_b->record_only)
2867 {
2868 /* Ok, we are done for this node and its descendants */
2869 *skip = TRUE;
2870 *skip_children = TRUE;
2871 }
2872 else if (! merge_b->dry_run)
2873 {
2874 /* Create the directory on disk, to allow descendants to be added */
2875 if (! db->add_existing)
2876 SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT,
2877 scratch_pool));
2878
2879 if (old_tc)
2880 {
2881 /* svn_wc_add4 and svn_wc_add_from_disk3 can't add a node
2882 over an existing tree conflict */
2883
2884 /* ### These functions should take some tree conflict argument
2885 and allow overwriting the tc when one is passed */
2886
2887 SVN_ERR(svn_wc__del_tree_conflict(merge_b->ctx->wc_ctx,
2888 local_abspath,
2889 scratch_pool));
2890 }
2891
2892 if (merge_b->same_repos)
2893 {
2894 const char *original_url;
2895
2896 original_url = svn_path_url_add_component2(
2897 merge_b->merge_source.loc2->url,
2898 relpath, scratch_pool);
2899
2900 /* Limitation (aka HACK):
2901 We create a newly added directory with an original URL and
2902 revision as that in the repository, but without its properties
2903 and children.
2904
2905 When the merge is cancelled before the final dir_added(), the
2906 copy won't really represent the in-repository state of the node.
2907 */
2908 SVN_ERR(svn_wc_add4(merge_b->ctx->wc_ctx, local_abspath,
2909 svn_depth_infinity,
2910 original_url,
2911 right_source->revision,
2912 merge_b->ctx->cancel_func,
2913 merge_b->ctx->cancel_baton,
2914 NULL, NULL /* no notify! */,
2915 scratch_pool));
2916 }
2917 else
2918 {
2919 SVN_ERR(svn_wc_add_from_disk3(merge_b->ctx->wc_ctx, local_abspath,
2920 apr_hash_make(scratch_pool),
2921 FALSE /* skip checks */,
2922 NULL, NULL /* no notify! */,
2923 scratch_pool));
2924 }
2925
2926 if (old_tc != NULL)
2927 {
2928 /* ### Should be atomic with svn_wc_add(4|_from_disk2)() */
2929 SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
2930 old_tc->node_kind,
2931 svn_node_none,
2932 svn_node_dir,
2933 db->tree_conflict_action,
2934 db->tree_conflict_reason,
2935 old_tc, FALSE,
2936 scratch_pool));
2937 }
2938 }
2939
2940 if (! db->shadowed && !merge_b->record_only)
2941 SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_dir,
2942 db->add_is_replace, scratch_pool));
2943 }
2944 return SVN_NO_ERROR;
2945 }
2946
2947 /* An svn_diff_tree_processor_t function.
2948 *
2949 * Called after merge_dir_opened() when a node exists in both the left and
2950 * right source, but has its properties changed inbetween.
2951 *
2952 * After the merge_dir_opened() but before the call to this merge_dir_changed()
2953 * function all descendants will have been updated.
2954 */
2955 static svn_error_t *
merge_dir_changed(const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,apr_hash_t * left_props,apr_hash_t * right_props,const apr_array_header_t * prop_changes,void * dir_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2956 merge_dir_changed(const char *relpath,
2957 const svn_diff_source_t *left_source,
2958 const svn_diff_source_t *right_source,
2959 /*const*/ apr_hash_t *left_props,
2960 /*const*/ apr_hash_t *right_props,
2961 const apr_array_header_t *prop_changes,
2962 void *dir_baton,
2963 const struct svn_diff_tree_processor_t *processor,
2964 apr_pool_t *scratch_pool)
2965 {
2966 merge_cmd_baton_t *merge_b = processor->baton;
2967 struct merge_dir_baton_t *db = dir_baton;
2968 const apr_array_header_t *props;
2969 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2970 relpath, scratch_pool);
2971
2972 SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
2973
2974 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2975
2976 if (db->shadowed)
2977 {
2978 if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
2979 {
2980 /* We haven't notified for this node yet: report a skip */
2981 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
2982 svn_wc_notify_update_shadowed_update,
2983 db->skip_reason, db->parent_baton,
2984 scratch_pool));
2985 }
2986
2987 return SVN_NO_ERROR;
2988 }
2989
2990 SVN_ERR(prepare_merge_props_changed(&props, local_abspath, prop_changes,
2991 merge_b, scratch_pool, scratch_pool));
2992
2993 if (props->nelts)
2994 {
2995 const svn_wc_conflict_version_t *left;
2996 const svn_wc_conflict_version_t *right;
2997 svn_client_ctx_t *ctx = merge_b->ctx;
2998 svn_wc_notify_state_t prop_state;
2999
3000 SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
3001 svn_node_dir, svn_node_dir,
3002 &merge_b->merge_source,
3003 merge_b->target,
3004 scratch_pool, scratch_pool));
3005
3006 SVN_ERR(svn_wc_merge_props3(&prop_state, ctx->wc_ctx, local_abspath,
3007 left, right,
3008 left_props, props,
3009 merge_b->dry_run,
3010 NULL, NULL,
3011 ctx->cancel_func, ctx->cancel_baton,
3012 scratch_pool));
3013
3014 if (prop_state == svn_wc_notify_state_conflicted)
3015 {
3016 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
3017 merge_b->pool);
3018 }
3019
3020 if (prop_state == svn_wc_notify_state_conflicted
3021 || prop_state == svn_wc_notify_state_merged
3022 || prop_state == svn_wc_notify_state_changed)
3023 {
3024 SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
3025 svn_wc_notify_state_inapplicable,
3026 prop_state, scratch_pool));
3027 }
3028 }
3029
3030 return SVN_NO_ERROR;
3031 }
3032
3033
3034 /* An svn_diff_tree_processor_t function.
3035 *
3036 * Called after merge_dir_opened() when a node doesn't exist in LEFT_SOURCE,
3037 * but does in RIGHT_SOURCE. After the merge_dir_opened() but before the call
3038 * to this merge_dir_added() function all descendants will have been added.
3039 *
3040 * When a node is replaced instead of just added a separate opened+deleted will
3041 * be invoked before the current open+added.
3042 */
3043 static svn_error_t *
merge_dir_added(const char * relpath,const svn_diff_source_t * copyfrom_source,const svn_diff_source_t * right_source,apr_hash_t * copyfrom_props,apr_hash_t * right_props,void * dir_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)3044 merge_dir_added(const char *relpath,
3045 const svn_diff_source_t *copyfrom_source,
3046 const svn_diff_source_t *right_source,
3047 /*const*/ apr_hash_t *copyfrom_props,
3048 /*const*/ apr_hash_t *right_props,
3049 void *dir_baton,
3050 const struct svn_diff_tree_processor_t *processor,
3051 apr_pool_t *scratch_pool)
3052 {
3053 merge_cmd_baton_t *merge_b = processor->baton;
3054 struct merge_dir_baton_t *db = dir_baton;
3055 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
3056 relpath, scratch_pool);
3057
3058 /* For consistency; usually a no-op from _dir_added() */
3059 SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
3060 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
3061
3062 if (db->shadowed)
3063 {
3064 if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
3065 {
3066 /* We haven't notified for this node yet: report a skip */
3067 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
3068 svn_wc_notify_update_shadowed_add,
3069 db->skip_reason, db->parent_baton,
3070 scratch_pool));
3071 }
3072
3073 return SVN_NO_ERROR;
3074 }
3075
3076 SVN_ERR_ASSERT(
3077 db->edited /* Marked edited from merge_open_dir() */
3078 && ! merge_b->record_only /* Skip details from merge_open_dir() */
3079 );
3080
3081 if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
3082 && ( !db->parent_baton || !db->parent_baton->added))
3083 {
3084 /* Store the roots of added subtrees */
3085 store_path(merge_b->added_abspaths, local_abspath);
3086 }
3087
3088 if (merge_b->same_repos)
3089 {
3090 /* When the directory was added in merge_dir_added() we didn't update its
3091 pristine properties. Instead we receive the property changes later and
3092 apply them in this function.
3093
3094 If we would apply them as changes (such as before fixing issue #3405),
3095 we would see the unmodified properties as local changes, and the
3096 pristine properties would be out of sync with what the repository
3097 expects for this directory.
3098
3099 Instead of doing that we now simply set the properties as the pristine
3100 properties via a private libsvn_wc api.
3101 */
3102
3103 const char *copyfrom_url;
3104 svn_revnum_t copyfrom_rev;
3105 const char *parent_abspath;
3106 const char *child;
3107
3108 /* Creating a hash containing regular and entry props */
3109 apr_hash_t *new_pristine_props = right_props;
3110
3111 parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3112 child = svn_dirent_is_child(merge_b->target->abspath, local_abspath, NULL);
3113 SVN_ERR_ASSERT(child != NULL);
3114
3115 copyfrom_url = svn_path_url_add_component2(merge_b->merge_source.loc2->url,
3116 child, scratch_pool);
3117 copyfrom_rev = right_source->revision;
3118
3119 SVN_ERR(check_repos_match(merge_b->target, parent_abspath, copyfrom_url,
3120 scratch_pool));
3121
3122 if (!merge_b->dry_run)
3123 {
3124 SVN_ERR(svn_wc__complete_directory_add(merge_b->ctx->wc_ctx,
3125 local_abspath,
3126 new_pristine_props,
3127 copyfrom_url, copyfrom_rev,
3128 scratch_pool));
3129 }
3130
3131 if (svn_hash_gets(new_pristine_props, SVN_PROP_MERGEINFO))
3132 {
3133 alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
3134 local_abspath, merge_b->pool);
3135 }
3136 }
3137 else
3138 {
3139 apr_array_header_t *regular_props;
3140 apr_hash_t *new_props;
3141 svn_wc_notify_state_t prop_state;
3142
3143 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props,
3144 scratch_pool),
3145 NULL, NULL, ®ular_props, scratch_pool));
3146
3147 new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
3148
3149 svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
3150
3151 /* ### What is the easiest way to set new_props on LOCAL_ABSPATH?
3152
3153 ### This doesn't need a merge as we just added the node
3154 ### (or installed a tree conflict and skipped this node)*/
3155
3156 SVN_ERR(svn_wc_merge_props3(&prop_state, merge_b->ctx->wc_ctx,
3157 local_abspath,
3158 NULL, NULL,
3159 apr_hash_make(scratch_pool),
3160 svn_prop_hash_to_array(new_props,
3161 scratch_pool),
3162 merge_b->dry_run,
3163 NULL, NULL,
3164 merge_b->ctx->cancel_func,
3165 merge_b->ctx->cancel_baton,
3166 scratch_pool));
3167 if (prop_state == svn_wc_notify_state_conflicted)
3168 {
3169 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
3170 merge_b->pool);
3171 }
3172 }
3173
3174 return SVN_NO_ERROR;
3175 }
3176
3177 /* Helper for merge_dir_deleted. Implement svn_wc_status_func4_t */
3178 static svn_error_t *
verify_touched_by_del_check(void * baton,const char * local_abspath,const svn_wc_status3_t * status,apr_pool_t * scratch_pool)3179 verify_touched_by_del_check(void *baton,
3180 const char *local_abspath,
3181 const svn_wc_status3_t *status,
3182 apr_pool_t *scratch_pool)
3183 {
3184 struct dir_delete_baton_t *delb = baton;
3185
3186 if (svn_hash_gets(delb->compared_abspaths, local_abspath))
3187 return SVN_NO_ERROR;
3188
3189 switch (status->node_status)
3190 {
3191 case svn_wc_status_deleted:
3192 case svn_wc_status_ignored:
3193 case svn_wc_status_none:
3194 return SVN_NO_ERROR;
3195
3196 default:
3197 delb->found_edit = TRUE;
3198 return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
3199 }
3200 }
3201
3202 /* An svn_diff_tree_processor_t function.
3203 *
3204 * Called after merge_dir_opened() when a node existed only in the left source.
3205 *
3206 * After the merge_dir_opened() but before the call to this merge_dir_deleted()
3207 * function all descendants that existed in left_source will have been deleted.
3208 *
3209 * If this node is replaced, an _opened() followed by a matching _add() will
3210 * be invoked after this function.
3211 */
3212 static svn_error_t *
merge_dir_deleted(const char * relpath,const svn_diff_source_t * left_source,apr_hash_t * left_props,void * dir_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)3213 merge_dir_deleted(const char *relpath,
3214 const svn_diff_source_t *left_source,
3215 /*const*/ apr_hash_t *left_props,
3216 void *dir_baton,
3217 const struct svn_diff_tree_processor_t *processor,
3218 apr_pool_t *scratch_pool)
3219 {
3220 merge_cmd_baton_t *merge_b = processor->baton;
3221 struct merge_dir_baton_t *db = dir_baton;
3222 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
3223 relpath, scratch_pool);
3224 svn_boolean_t same;
3225 apr_hash_t *working_props;
3226
3227 SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
3228 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
3229
3230 if (db->shadowed)
3231 {
3232 if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
3233 {
3234 /* We haven't notified for this node yet: report a skip */
3235 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
3236 svn_wc_notify_update_shadowed_delete,
3237 db->skip_reason, db->parent_baton,
3238 scratch_pool));
3239 }
3240
3241 return SVN_NO_ERROR;
3242 }
3243
3244 /* Easy out: We are only applying mergeinfo differences. */
3245 if (merge_b->record_only)
3246 {
3247 return SVN_NO_ERROR;
3248 }
3249
3250 SVN_ERR(svn_wc_prop_list2(&working_props,
3251 merge_b->ctx->wc_ctx, local_abspath,
3252 scratch_pool, scratch_pool));
3253
3254 if (merge_b->force_delete)
3255 {
3256 /* In this legacy mode we just assume that a directory delete
3257 matches any directory. db->delete_state is NULL */
3258 same = TRUE;
3259 }
3260 else
3261 {
3262 struct dir_delete_baton_t *delb;
3263
3264 /* Compare the properties */
3265 SVN_ERR(properties_same_p(&same, left_props, working_props,
3266 scratch_pool));
3267 delb = db->delete_state;
3268 assert(delb != NULL);
3269
3270 if (! same)
3271 {
3272 delb->found_edit = TRUE;
3273 }
3274 else
3275 {
3276 store_path(delb->compared_abspaths, local_abspath);
3277 }
3278
3279 if (delb->del_root != db)
3280 return SVN_NO_ERROR;
3281
3282 if (delb->found_edit)
3283 same = FALSE;
3284 else
3285 {
3286 apr_array_header_t *ignores;
3287 svn_error_t *err;
3288 same = TRUE;
3289
3290 SVN_ERR(svn_wc_get_default_ignores(&ignores, merge_b->ctx->config,
3291 scratch_pool));
3292
3293 /* None of the descendants was modified, but maybe there are
3294 descendants we haven't walked?
3295
3296 Note that we aren't interested in changes, as we already verified
3297 changes in the paths touched by the merge. And the existence of
3298 other paths is enough to mark the directory edited */
3299 err = svn_wc_walk_status(merge_b->ctx->wc_ctx, local_abspath,
3300 svn_depth_infinity, TRUE /* get-all */,
3301 FALSE /* no-ignore */,
3302 TRUE /* ignore-text-mods */, ignores,
3303 verify_touched_by_del_check, delb,
3304 merge_b->ctx->cancel_func,
3305 merge_b->ctx->cancel_baton,
3306 scratch_pool);
3307
3308 if (err)
3309 {
3310 if (err->apr_err != SVN_ERR_CEASE_INVOCATION)
3311 return svn_error_trace(err);
3312
3313 svn_error_clear(err);
3314 }
3315
3316 same = ! delb->found_edit;
3317 }
3318 }
3319
3320 if (same && !merge_b->dry_run)
3321 {
3322 svn_error_t *err;
3323
3324 err = svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
3325 FALSE /* keep_local */, FALSE /* unversioned */,
3326 merge_b->ctx->cancel_func,
3327 merge_b->ctx->cancel_baton,
3328 NULL, NULL /* no notify */,
3329 scratch_pool);
3330
3331 if (err)
3332 {
3333 if (err->apr_err != SVN_ERR_WC_LEFT_LOCAL_MOD)
3334 return svn_error_trace(err);
3335
3336 svn_error_clear(err);
3337 same = FALSE;
3338 }
3339 }
3340
3341 if (! same)
3342 {
3343 /* If the attempt to delete an existing directory failed,
3344 * the directory has local modifications (e.g. locally added
3345 * files, or property changes). Flag a tree conflict. */
3346
3347 /* This handles use case 5 described in the paper attached to issue
3348 * #2282. See also notes/tree-conflicts/detection.txt
3349 */
3350 SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
3351 svn_node_dir,
3352 svn_node_dir,
3353 svn_node_none,
3354 svn_wc_conflict_action_delete,
3355 svn_wc_conflict_reason_edited,
3356 NULL, TRUE,
3357 scratch_pool));
3358 }
3359 else
3360 {
3361 /* Record that we might have deleted mergeinfo */
3362 if (working_props
3363 && svn_hash_gets(working_props, SVN_PROP_MERGEINFO))
3364 {
3365 alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
3366 local_abspath, merge_b->pool);
3367 }
3368
3369 SVN_ERR(record_update_delete(merge_b, db->parent_baton, local_abspath,
3370 svn_node_dir, scratch_pool));
3371 }
3372
3373 return SVN_NO_ERROR;
3374 }
3375
3376 /* An svn_diff_tree_processor_t function.
3377 *
3378 * Called after merge_dir_opened() when a node itself didn't change between
3379 * the left and right source.
3380 *
3381 * After the merge_dir_opened() but before the call to this merge_dir_closed()
3382 * function all descendants will have been processed.
3383 */
3384 static svn_error_t *
merge_dir_closed(const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,void * dir_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)3385 merge_dir_closed(const char *relpath,
3386 const svn_diff_source_t *left_source,
3387 const svn_diff_source_t *right_source,
3388 void *dir_baton,
3389 const struct svn_diff_tree_processor_t *processor,
3390 apr_pool_t *scratch_pool)
3391 {
3392 merge_cmd_baton_t *merge_b = processor->baton;
3393 struct merge_dir_baton_t *db = dir_baton;
3394
3395 SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
3396
3397 return SVN_NO_ERROR;
3398 }
3399
3400 /* An svn_diff_tree_processor_t function.
3401
3402 Called when the diff driver wants to report an absent path.
3403
3404 In case of merges this happens when the diff encounters a server-excluded
3405 path.
3406
3407 We register a skipped path, which will make parent mergeinfo non-
3408 inheritable. This ensures that a future merge might see these skipped
3409 changes as eligable for merging.
3410
3411 For legacy reasons we also notify the path as skipped.
3412 */
3413 static svn_error_t *
merge_node_absent(const char * relpath,void * dir_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)3414 merge_node_absent(const char *relpath,
3415 void *dir_baton,
3416 const svn_diff_tree_processor_t *processor,
3417 apr_pool_t *scratch_pool)
3418 {
3419 merge_cmd_baton_t *merge_b = processor->baton;
3420 struct merge_dir_baton_t *db = dir_baton;
3421
3422 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
3423 relpath, scratch_pool);
3424
3425 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_unknown,
3426 svn_wc_notify_skip, svn_wc_notify_state_missing,
3427 db, scratch_pool));
3428
3429 return SVN_NO_ERROR;
3430 }
3431
3432 /* Return a diff processor that will apply the merge to the WC.
3433 */
3434 static svn_diff_tree_processor_t *
merge_apply_processor(merge_cmd_baton_t * merge_cmd_baton,apr_pool_t * result_pool)3435 merge_apply_processor(merge_cmd_baton_t *merge_cmd_baton,
3436 apr_pool_t *result_pool)
3437 {
3438 svn_diff_tree_processor_t *merge_processor;
3439
3440 merge_processor = svn_diff__tree_processor_create(merge_cmd_baton,
3441 result_pool);
3442
3443 merge_processor->dir_opened = merge_dir_opened;
3444 merge_processor->dir_changed = merge_dir_changed;
3445 merge_processor->dir_added = merge_dir_added;
3446 merge_processor->dir_deleted = merge_dir_deleted;
3447 merge_processor->dir_closed = merge_dir_closed;
3448
3449 merge_processor->file_opened = merge_file_opened;
3450 merge_processor->file_changed = merge_file_changed;
3451 merge_processor->file_added = merge_file_added;
3452 merge_processor->file_deleted = merge_file_deleted;
3453 /* Not interested in file_closed() */
3454
3455 merge_processor->node_absent = merge_node_absent;
3456
3457 return merge_processor;
3458 }
3459
3460 /* Initialize minimal dir baton to allow calculating 'R'eplace
3461 from 'D'elete + 'A'dd. */
3462 static void *
open_dir_for_replace_single_file(apr_pool_t * result_pool)3463 open_dir_for_replace_single_file(apr_pool_t *result_pool)
3464 {
3465 struct merge_dir_baton_t *dir_baton = apr_pcalloc(result_pool, sizeof(*dir_baton));
3466
3467 dir_baton->pool = result_pool;
3468 dir_baton->tree_conflict_reason = CONFLICT_REASON_NONE;
3469 dir_baton->tree_conflict_action = svn_wc_conflict_action_edit;
3470 dir_baton->skip_reason = svn_wc_notify_state_unknown;
3471
3472 return dir_baton;
3473 }
3474
3475 /*-----------------------------------------------------------------------*/
3476
3477 /*** Merge Notification ***/
3478
3479
3480 /* Finds a nearest ancestor in CHILDREN_WITH_MERGEINFO for LOCAL_ABSPATH. If
3481 PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
3482 where child->abspath == PATH is considered PATH's ancestor. If FALSE,
3483 then child->abspath must be a proper ancestor of PATH.
3484
3485 CHILDREN_WITH_MERGEINFO is expected to be sorted in Depth first
3486 order of path. */
3487 static svn_client__merge_path_t *
find_nearest_ancestor(const apr_array_header_t * children_with_mergeinfo,svn_boolean_t path_is_own_ancestor,const char * local_abspath)3488 find_nearest_ancestor(const apr_array_header_t *children_with_mergeinfo,
3489 svn_boolean_t path_is_own_ancestor,
3490 const char *local_abspath)
3491 {
3492 int i;
3493
3494 SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
3495
3496 for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
3497 {
3498 svn_client__merge_path_t *child =
3499 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
3500
3501 if (svn_dirent_is_ancestor(child->abspath, local_abspath)
3502 && (path_is_own_ancestor
3503 || strcmp(child->abspath, local_abspath) != 0))
3504 return child;
3505 }
3506 return NULL;
3507 }
3508
3509 /* Find the highest level path in a merge target (possibly the merge target
3510 itself) to use in a merge notification header.
3511
3512 Return the svn_client__merge_path_t * representing the most distant
3513 ancestor in CHILDREN_WITH_MERGEINFO of LOCAL_ABSPATH where said
3514 ancestor's first remaining ranges element (per the REMAINING_RANGES
3515 member of the ancestor) intersect with the first remaining ranges element
3516 for every intermediate ancestor svn_client__merge_path_t * of
3517 LOCAL_ABSPATH. If no such ancestor is found return NULL.
3518
3519 If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
3520 represent a forward merge, then set *START to the oldest revision found
3521 in any of the intersecting ancestors and *END to the youngest revision
3522 found. If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
3523 represent a reverse merge, then set *START to the youngest revision
3524 found and *END to the oldest revision found. If no ancestors are found
3525 then set *START and *END to SVN_INVALID_REVNUM.
3526
3527 If PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
3528 where child->abspath == PATH is considered PATH's ancestor. If FALSE,
3529 then child->abspath must be a proper ancestor of PATH.
3530
3531 See the CHILDREN_WITH_MERGEINFO ARRAY global comment for more
3532 information. */
3533 static svn_client__merge_path_t *
find_nearest_ancestor_with_intersecting_ranges(svn_revnum_t * start,svn_revnum_t * end,const apr_array_header_t * children_with_mergeinfo,svn_boolean_t path_is_own_ancestor,const char * local_abspath)3534 find_nearest_ancestor_with_intersecting_ranges(
3535 svn_revnum_t *start,
3536 svn_revnum_t *end,
3537 const apr_array_header_t *children_with_mergeinfo,
3538 svn_boolean_t path_is_own_ancestor,
3539 const char *local_abspath)
3540 {
3541 int i;
3542 svn_client__merge_path_t *nearest_ancestor = NULL;
3543
3544 *start = SVN_INVALID_REVNUM;
3545 *end = SVN_INVALID_REVNUM;
3546
3547 SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
3548
3549 for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
3550 {
3551 svn_client__merge_path_t *child =
3552 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
3553
3554 if (svn_dirent_is_ancestor(child->abspath, local_abspath)
3555 && (path_is_own_ancestor
3556 || strcmp(child->abspath, local_abspath) != 0))
3557 {
3558 if (nearest_ancestor == NULL)
3559 {
3560 /* Found an ancestor. */
3561 nearest_ancestor = child;
3562
3563 if (child->remaining_ranges)
3564 {
3565 svn_merge_range_t *r1 = APR_ARRAY_IDX(
3566 child->remaining_ranges, 0, svn_merge_range_t *);
3567 *start = r1->start;
3568 *end = r1->end;
3569 }
3570 else
3571 {
3572 /* If CHILD->REMAINING_RANGES is null then LOCAL_ABSPATH
3573 is inside an absent subtree in the merge target. */
3574 *start = SVN_INVALID_REVNUM;
3575 *end = SVN_INVALID_REVNUM;
3576 break;
3577 }
3578 }
3579 else
3580 {
3581 /* We'e found another ancestor for LOCAL_ABSPATH. Do its
3582 first remaining range intersect with the previously
3583 found ancestor? */
3584 svn_merge_range_t *r1 =
3585 APR_ARRAY_IDX(nearest_ancestor->remaining_ranges, 0,
3586 svn_merge_range_t *);
3587 svn_merge_range_t *r2 =
3588 APR_ARRAY_IDX(child->remaining_ranges, 0,
3589 svn_merge_range_t *);
3590
3591 if (r1 && r2)
3592 {
3593 svn_merge_range_t range1;
3594 svn_merge_range_t range2;
3595 svn_boolean_t reverse_merge = r1->start > r2->end;
3596
3597 /* Flip endpoints if this is a reverse merge. */
3598 if (reverse_merge)
3599 {
3600 range1.start = r1->end;
3601 range1.end = r1->start;
3602 range2.start = r2->end;
3603 range2.end = r2->start;
3604 }
3605 else
3606 {
3607 range1.start = r1->start;
3608 range1.end = r1->end;
3609 range2.start = r2->start;
3610 range2.end = r2->end;
3611 }
3612
3613 if (range1.start < range2.end && range2.start < range1.end)
3614 {
3615 *start = reverse_merge ?
3616 MAX(r1->start, r2->start) : MIN(r1->start, r2->start);
3617 *end = reverse_merge ?
3618 MIN(r1->end, r2->end) : MAX(r1->end, r2->end);
3619 nearest_ancestor = child;
3620 }
3621 }
3622 }
3623 }
3624 }
3625 return nearest_ancestor;
3626 }
3627
3628 /* Notify that we're starting to record mergeinfo for the merge of the
3629 * revision range RANGE into TARGET_ABSPATH. RANGE should be null if the
3630 * merge sources are not from the same URL.
3631 *
3632 * This calls the client's notification receiver (as found in the client
3633 * context), with a WC abspath.
3634 */
3635 static void
notify_mergeinfo_recording(const char * target_abspath,const svn_merge_range_t * range,svn_client_ctx_t * ctx,apr_pool_t * pool)3636 notify_mergeinfo_recording(const char *target_abspath,
3637 const svn_merge_range_t *range,
3638 svn_client_ctx_t *ctx,
3639 apr_pool_t *pool)
3640 {
3641 if (ctx->notify_func2)
3642 {
3643 svn_wc_notify_t *n = svn_wc_create_notify(
3644 target_abspath, svn_wc_notify_merge_record_info_begin, pool);
3645
3646 n->merge_range = range ? svn_merge_range_dup(range, pool) : NULL;
3647 ctx->notify_func2(ctx->notify_baton2, n, pool);
3648 }
3649 }
3650
3651 /* Notify that we're completing the merge into TARGET_ABSPATH.
3652 *
3653 * This calls the client's notification receiver (as found in the client
3654 * context), with a WC abspath.
3655 */
3656 static void
notify_merge_completed(const char * target_abspath,svn_client_ctx_t * ctx,apr_pool_t * pool)3657 notify_merge_completed(const char *target_abspath,
3658 svn_client_ctx_t *ctx,
3659 apr_pool_t *pool)
3660 {
3661 if (ctx->notify_func2)
3662 {
3663 svn_wc_notify_t *n
3664 = svn_wc_create_notify(target_abspath, svn_wc_notify_merge_completed,
3665 pool);
3666 ctx->notify_func2(ctx->notify_baton2, n, pool);
3667 }
3668 }
3669
3670
3671 /* Remove merge source gaps from range used for merge notifications.
3672 See https://issues.apache.org/jira/browse/SVN-4138
3673
3674 If IMPLICIT_SRC_GAP is not NULL then it is a rangelist containing a
3675 single range (see the implicit_src_gap member of merge_cmd_baton_t).
3676 RANGE describes a (possibly reverse) merge.
3677
3678 If IMPLICIT_SRC_GAP is not NULL and it's sole range intersects with
3679 the older revision in *RANGE, then remove IMPLICIT_SRC_GAP's range
3680 from *RANGE. */
3681 static void
remove_source_gap(svn_merge_range_t * range,apr_array_header_t * implicit_src_gap)3682 remove_source_gap(svn_merge_range_t *range,
3683 apr_array_header_t *implicit_src_gap)
3684 {
3685 if (implicit_src_gap)
3686 {
3687 svn_merge_range_t *gap_range =
3688 APR_ARRAY_IDX(implicit_src_gap, 0, svn_merge_range_t *);
3689 if (range->start < range->end)
3690 {
3691 if (gap_range->start == range->start)
3692 range->start = gap_range->end;
3693 }
3694 else /* Reverse merge */
3695 {
3696 if (gap_range->start == range->end)
3697 range->end = gap_range->end;
3698 }
3699 }
3700 }
3701
3702 /* Notify that we're starting a merge
3703 *
3704 * This calls the client's notification receiver (as found in the client
3705 * context), with a WC abspath.
3706 */
3707 static void
notify_merge_begin(struct notify_begin_state_t * notify_begin_state,const char * local_abspath,svn_boolean_t delete_action,apr_pool_t * scratch_pool)3708 notify_merge_begin(struct notify_begin_state_t *notify_begin_state,
3709 const char *local_abspath,
3710 svn_boolean_t delete_action,
3711 apr_pool_t *scratch_pool)
3712 {
3713 merge_cmd_baton_t *merge_b = notify_begin_state->merge_b;
3714 svn_wc_notify_t *notify;
3715 svn_merge_range_t n_range =
3716 {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE};
3717 const char *notify_abspath;
3718
3719 if (! notify_begin_state->notify_func2)
3720 return;
3721
3722 /* If our merge sources are ancestors of one another... */
3723 if (merge_b->merge_source.ancestral)
3724 {
3725 const svn_client__merge_path_t *child;
3726 /* Find LOCAL_ABSPATH's nearest ancestor in
3727 CHILDREN_WITH_MERGEINFO. Normally we consider a child in
3728 CHILDREN_WITH_MERGEINFO representing PATH to be an
3729 ancestor of PATH, but if this is a deletion of PATH then the
3730 notification must be for a proper ancestor of PATH. This ensures
3731 we don't get notifications like:
3732
3733 --- Merging rX into 'PARENT/CHILD'
3734 D PARENT/CHILD
3735
3736 But rather:
3737
3738 --- Merging rX into 'PARENT'
3739 D PARENT/CHILD
3740 */
3741
3742 child = find_nearest_ancestor_with_intersecting_ranges(
3743 &(n_range.start), &(n_range.end),
3744 merge_b->children_with_mergeinfo,
3745 ! delete_action, local_abspath);
3746
3747 if (!child && delete_action)
3748 {
3749 /* Triggered by file replace in single-file-merge */
3750 child = find_nearest_ancestor(merge_b->children_with_mergeinfo,
3751 TRUE, local_abspath);
3752 }
3753
3754 assert(child != NULL); /* Should always find the merge anchor */
3755
3756 if (! child)
3757 return;
3758
3759 if (notify_begin_state->last_abspath != NULL
3760 && strcmp(child->abspath, notify_begin_state->last_abspath) == 0)
3761 {
3762 /* Don't notify the same merge again */
3763 return;
3764 }
3765
3766 notify_begin_state->last_abspath = child->abspath;
3767
3768 if (child->absent || child->remaining_ranges->nelts == 0
3769 || !SVN_IS_VALID_REVNUM(n_range.start))
3770 {
3771 /* No valid information for an header */
3772 return;
3773 }
3774
3775 notify_abspath = child->abspath;
3776 }
3777 else
3778 {
3779 if (notify_begin_state->last_abspath)
3780 return; /* already notified */
3781
3782 notify_abspath = merge_b->target->abspath;
3783 /* Store something in last_abspath. Any value would do */
3784 notify_begin_state->last_abspath = merge_b->target->abspath;
3785 }
3786
3787 notify = svn_wc_create_notify(notify_abspath,
3788 merge_b->same_repos
3789 ? svn_wc_notify_merge_begin
3790 : svn_wc_notify_foreign_merge_begin,
3791 scratch_pool);
3792
3793 if (SVN_IS_VALID_REVNUM(n_range.start))
3794 {
3795 /* If the merge source has a gap, then don't mention
3796 those gap revisions in the notification. */
3797 remove_source_gap(&n_range, merge_b->implicit_src_gap);
3798 notify->merge_range = &n_range;
3799 }
3800 else
3801 {
3802 notify->merge_range = NULL;
3803 }
3804
3805 notify_begin_state->notify_func2(notify_begin_state->notify_baton2, notify,
3806 scratch_pool);
3807 }
3808
3809 /* Our notification callback, that adds a 'begin' notification */
3810 static void
notify_merging(void * baton,const svn_wc_notify_t * notify,apr_pool_t * pool)3811 notify_merging(void *baton,
3812 const svn_wc_notify_t *notify,
3813 apr_pool_t *pool)
3814 {
3815 struct notify_begin_state_t *b = baton;
3816
3817 notify_merge_begin(b, notify->path,
3818 notify->action == svn_wc_notify_update_delete,
3819 pool);
3820
3821 b->notify_func2(b->notify_baton2, notify, pool);
3822 }
3823
3824 /* Set *OUT_RANGELIST to the intersection of IN_RANGELIST with the simple
3825 * (inheritable) revision range REV1:REV2, according to CONSIDER_INHERITANCE.
3826 * If REV1 is equal to REV2, the result is an empty rangelist, otherwise
3827 * REV1 must be less than REV2.
3828 *
3829 * Note: If CONSIDER_INHERITANCE is FALSE, the effect is to treat any non-
3830 * inheritable input ranges as if they were inheritable. If it is TRUE, the
3831 * effect is to discard any non-inheritable input ranges. Therefore the
3832 * ranges in *OUT_RANGELIST will always be inheritable. */
3833 static svn_error_t *
rangelist_intersect_range(svn_rangelist_t ** out_rangelist,const svn_rangelist_t * in_rangelist,svn_revnum_t rev1,svn_revnum_t rev2,svn_boolean_t consider_inheritance,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3834 rangelist_intersect_range(svn_rangelist_t **out_rangelist,
3835 const svn_rangelist_t *in_rangelist,
3836 svn_revnum_t rev1,
3837 svn_revnum_t rev2,
3838 svn_boolean_t consider_inheritance,
3839 apr_pool_t *result_pool,
3840 apr_pool_t *scratch_pool)
3841 {
3842 SVN_ERR_ASSERT(rev1 <= rev2);
3843
3844 if (rev1 < rev2)
3845 {
3846 svn_rangelist_t *simple_rangelist =
3847 svn_rangelist__initialize(rev1, rev2, TRUE, scratch_pool);
3848
3849 SVN_ERR(svn_rangelist_intersect(out_rangelist,
3850 simple_rangelist, in_rangelist,
3851 consider_inheritance, result_pool));
3852 }
3853 else
3854 {
3855 *out_rangelist = apr_array_make(result_pool, 0,
3856 sizeof(svn_merge_range_t *));
3857 }
3858 return SVN_NO_ERROR;
3859 }
3860
3861 /* Helper for fix_deleted_subtree_ranges(). Like fix_deleted_subtree_ranges()
3862 this function should only be called when honoring mergeinfo.
3863
3864 CHILD, PARENT, REVISION1, REVISION2, and RA_SESSION are all cascaded from
3865 fix_deleted_subtree_ranges() -- see that function for more information on
3866 each.
3867
3868 If PARENT is not the merge target then PARENT must have already have been
3869 processed by this function as a child. Specifically, this means that
3870 PARENT->REMAINING_RANGES must already be populated -- it can be an empty
3871 rangelist but cannot be NULL.
3872
3873 PRIMARY_URL is the merge source url of CHILD at the younger of REVISION1
3874 and REVISION2.
3875
3876 Since this function is only invoked for subtrees of the merge target, the
3877 guarantees afforded by normalize_merge_sources() don't apply - see the
3878 'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file.
3879 Therefore it is possible that PRIMARY_URL@REVISION1 and
3880 PRIMARY_URL@REVISION2 don't describe the endpoints of an unbroken line of
3881 history. The purpose of this helper is to identify these cases of broken
3882 history and adjust CHILD->REMAINING_RANGES in such a way we don't later try
3883 to describe nonexistent path/revisions to the merge report editor -- see
3884 drive_merge_report_editor().
3885
3886 If PRIMARY_URL@REVISION1 and PRIMARY_URL@REVISION2 describe an unbroken
3887 line of history then do nothing and leave CHILD->REMAINING_RANGES as-is.
3888
3889 If neither PRIMARY_URL@REVISION1 nor PRIMARY_URL@REVISION2 exist then
3890 there is nothing to merge to CHILD->ABSPATH so set CHILD->REMAINING_RANGES
3891 equal to PARENT->REMAINING_RANGES. This will cause the subtree to
3892 effectively ignore CHILD -- see 'Note: If the first svn_merge_range_t...'
3893 in drive_merge_report_editor()'s doc string.
3894
3895 If PRIMARY_URL@REVISION1 *xor* PRIMARY_URL@REVISION2 exist then we take the
3896 subset of REVISION1:REVISION2 in CHILD->REMAINING_RANGES at which
3897 PRIMARY_URL doesn't exist and set that subset equal to
3898 PARENT->REMAINING_RANGES' intersection with that non-existent range. Why?
3899 Because this causes CHILD->REMAINING_RANGES to be identical to
3900 PARENT->REMAINING_RANGES for revisions between REVISION1 and REVISION2 at
3901 which PRIMARY_URL doesn't exist. As mentioned above this means that
3902 drive_merge_report_editor() won't attempt to describe these non-existent
3903 subtree path/ranges to the reporter (which would break the merge).
3904
3905 If the preceding paragraph wasn't terribly clear then what follows spells
3906 out this function's behavior a bit more explicitly:
3907
3908 For forward merges (REVISION1 < REVISION2)
3909
3910 If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then
3911 find the revision 'N' in which PRIMARY_URL@REVISION1 was deleted. Leave
3912 the subset of CHILD->REMAINING_RANGES that intersects with
3913 REVISION1:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3914 that intersects with (N - 1):REVISION2 equal to PARENT->REMAINING_RANGES'
3915 intersection with (N - 1):REVISION2.
3916
3917 If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does,
3918 then find the revision 'M' in which PRIMARY_URL@REVISION2 came into
3919 existence. Leave the subset of CHILD->REMAINING_RANGES that intersects with
3920 (M - 1):REVISION2 as-is and set the subset of CHILD->REMAINING_RANGES
3921 that intersects with REVISION1:(M - 1) equal to PARENT->REMAINING_RANGES'
3922 intersection with REVISION1:(M - 1).
3923
3924 For reverse merges (REVISION1 > REVISION2)
3925
3926 If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then
3927 find the revision 'N' in which PRIMARY_URL@REVISION1 came into existence.
3928 Leave the subset of CHILD->REMAINING_RANGES that intersects with
3929 REVISION2:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3930 that intersects with (N - 1):REVISION1 equal to PARENT->REMAINING_RANGES'
3931 intersection with (N - 1):REVISION1.
3932
3933 If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does,
3934 then find the revision 'M' in which PRIMARY_URL@REVISION2 came into
3935 existence. Leave the subset of CHILD->REMAINING_RANGES that intersects with
3936 REVISION2:(M - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3937 that intersects with (M - 1):REVISION1 equal to PARENT->REMAINING_RANGES'
3938 intersection with REVISION1:(M - 1).
3939
3940 SCRATCH_POOL is used for all temporary allocations. Changes to CHILD are
3941 allocated in RESULT_POOL. */
3942 static svn_error_t *
adjust_deleted_subtree_ranges(svn_client__merge_path_t * child,svn_client__merge_path_t * parent,svn_revnum_t revision1,svn_revnum_t revision2,const char * primary_url,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3943 adjust_deleted_subtree_ranges(svn_client__merge_path_t *child,
3944 svn_client__merge_path_t *parent,
3945 svn_revnum_t revision1,
3946 svn_revnum_t revision2,
3947 const char *primary_url,
3948 svn_ra_session_t *ra_session,
3949 svn_client_ctx_t *ctx,
3950 apr_pool_t *result_pool,
3951 apr_pool_t *scratch_pool)
3952 {
3953 svn_boolean_t is_rollback = revision2 < revision1;
3954 svn_revnum_t younger_rev = is_rollback ? revision1 : revision2;
3955 svn_revnum_t peg_rev = younger_rev;
3956 svn_revnum_t older_rev = is_rollback ? revision2 : revision1;
3957 apr_array_header_t *segments;
3958 svn_error_t *err;
3959
3960 SVN_ERR_ASSERT(parent->remaining_ranges);
3961
3962 err = svn_client__repos_location_segments(&segments, ra_session,
3963 primary_url, peg_rev,
3964 younger_rev, older_rev, ctx,
3965 scratch_pool);
3966
3967 if (err)
3968 {
3969 const char *rel_source_path; /* PRIMARY_URL relative to RA_SESSION */
3970 svn_node_kind_t kind;
3971
3972 if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
3973 return svn_error_trace(err);
3974
3975 svn_error_clear(err);
3976
3977 /* PRIMARY_URL@peg_rev doesn't exist. Check if PRIMARY_URL@older_rev
3978 exists, if neither exist then the editor can simply ignore this
3979 subtree. */
3980
3981 SVN_ERR(svn_ra_get_path_relative_to_session(
3982 ra_session, &rel_source_path, primary_url, scratch_pool));
3983
3984 SVN_ERR(svn_ra_check_path(ra_session, rel_source_path,
3985 older_rev, &kind, scratch_pool));
3986 if (kind == svn_node_none)
3987 {
3988 /* Neither PRIMARY_URL@peg_rev nor PRIMARY_URL@older_rev exist,
3989 so there is nothing to merge. Set CHILD->REMAINING_RANGES
3990 identical to PARENT's. */
3991 child->remaining_ranges =
3992 svn_rangelist_dup(parent->remaining_ranges, scratch_pool);
3993 }
3994 else
3995 {
3996 svn_rangelist_t *deleted_rangelist;
3997 svn_revnum_t rev_primary_url_deleted;
3998
3999 /* PRIMARY_URL@older_rev exists, so it was deleted at some
4000 revision prior to peg_rev, find that revision. */
4001 SVN_ERR(svn_ra_get_deleted_rev(ra_session, rel_source_path,
4002 older_rev, younger_rev,
4003 &rev_primary_url_deleted,
4004 scratch_pool));
4005
4006 /* PRIMARY_URL@older_rev exists and PRIMARY_URL@peg_rev doesn't,
4007 so svn_ra_get_deleted_rev() should always find the revision
4008 PRIMARY_URL@older_rev was deleted. */
4009 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev_primary_url_deleted));
4010
4011 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
4012 PARENT->REMAINING_RANGES so both will work with the
4013 svn_rangelist_* APIs below. */
4014 if (is_rollback)
4015 {
4016 /* svn_rangelist_reverse operates in place so it's safe
4017 to use our scratch_pool. */
4018 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
4019 scratch_pool));
4020 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
4021 scratch_pool));
4022 }
4023
4024 /* Find the intersection of CHILD->REMAINING_RANGES with the
4025 range over which PRIMARY_URL@older_rev exists (ending at
4026 the youngest revision at which it still exists). */
4027 SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
4028 child->remaining_ranges,
4029 older_rev,
4030 rev_primary_url_deleted - 1,
4031 FALSE,
4032 scratch_pool, scratch_pool));
4033
4034 /* Merge into CHILD->REMAINING_RANGES the intersection of
4035 PARENT->REMAINING_RANGES with the range beginning when
4036 PRIMARY_URL@older_rev was deleted until younger_rev. */
4037 SVN_ERR(rangelist_intersect_range(&deleted_rangelist,
4038 parent->remaining_ranges,
4039 rev_primary_url_deleted - 1,
4040 peg_rev,
4041 FALSE,
4042 scratch_pool, scratch_pool));
4043 SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
4044 deleted_rangelist, scratch_pool,
4045 scratch_pool));
4046
4047 /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
4048 to reverse order if necessary. */
4049 if (is_rollback)
4050 {
4051 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
4052 scratch_pool));
4053 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
4054 scratch_pool));
4055 }
4056 }
4057 }
4058 else /* PRIMARY_URL@peg_rev exists. */
4059 {
4060 svn_rangelist_t *non_existent_rangelist;
4061 svn_location_segment_t *segment =
4062 APR_ARRAY_IDX(segments, (segments->nelts - 1),
4063 svn_location_segment_t *);
4064
4065 /* We know PRIMARY_URL@peg_rev exists as the call to
4066 svn_client__repos_location_segments() succeeded. If there is only
4067 one segment that starts at oldest_rev then we know that
4068 PRIMARY_URL@oldest_rev:PRIMARY_URL@peg_rev describes an unbroken
4069 line of history, so there is nothing more to adjust in
4070 CHILD->REMAINING_RANGES. */
4071 if (segment->range_start == older_rev)
4072 {
4073 return SVN_NO_ERROR;
4074 }
4075
4076 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
4077 PARENT->REMAINING_RANGES so both will work with the
4078 svn_rangelist_* APIs below. */
4079 if (is_rollback)
4080 {
4081 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
4082 scratch_pool));
4083 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
4084 scratch_pool));
4085 }
4086
4087 /* Intersect CHILD->REMAINING_RANGES with the range where PRIMARY_URL
4088 exists. Since segment doesn't span older_rev:peg_rev we know
4089 PRIMARY_URL@peg_rev didn't come into existence until
4090 segment->range_start + 1. */
4091 SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
4092 child->remaining_ranges,
4093 segment->range_start, peg_rev,
4094 FALSE, scratch_pool, scratch_pool));
4095
4096 /* Merge into CHILD->REMAINING_RANGES the intersection of
4097 PARENT->REMAINING_RANGES with the range before PRIMARY_URL@peg_rev
4098 came into existence. */
4099 SVN_ERR(rangelist_intersect_range(&non_existent_rangelist,
4100 parent->remaining_ranges,
4101 older_rev, segment->range_start,
4102 FALSE, scratch_pool, scratch_pool));
4103 SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
4104 non_existent_rangelist, scratch_pool,
4105 scratch_pool));
4106
4107 /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
4108 to reverse order if necessary. */
4109 if (is_rollback)
4110 {
4111 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
4112 scratch_pool));
4113 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
4114 scratch_pool));
4115 }
4116 }
4117
4118 /* Make a lasting copy of CHILD->REMAINING_RANGES using POOL. */
4119 child->remaining_ranges = svn_rangelist_dup(child->remaining_ranges,
4120 result_pool);
4121 return SVN_NO_ERROR;
4122 }
4123
4124 /* Helper for do_directory_merge().
4125
4126 SOURCE is cascaded from the argument of the same name in
4127 do_directory_merge(). TARGET is the merge target. RA_SESSION is the
4128 session for the younger of SOURCE->loc1 and SOURCE->loc2.
4129
4130 Adjust the subtrees in CHILDREN_WITH_MERGEINFO so that we don't
4131 later try to describe invalid paths in drive_merge_report_editor().
4132 This function is just a thin wrapper around
4133 adjust_deleted_subtree_ranges(), which see for further details.
4134
4135 SCRATCH_POOL is used for all temporary allocations. Changes to
4136 CHILDREN_WITH_MERGEINFO are allocated in RESULT_POOL.
4137 */
4138 static svn_error_t *
fix_deleted_subtree_ranges(const merge_source_t * source,const merge_target_t * target,svn_ra_session_t * ra_session,apr_array_header_t * children_with_mergeinfo,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4139 fix_deleted_subtree_ranges(const merge_source_t *source,
4140 const merge_target_t *target,
4141 svn_ra_session_t *ra_session,
4142 apr_array_header_t *children_with_mergeinfo,
4143 svn_client_ctx_t *ctx,
4144 apr_pool_t *result_pool,
4145 apr_pool_t *scratch_pool)
4146 {
4147 int i;
4148 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4149 svn_boolean_t is_rollback = source->loc2->rev < source->loc1->rev;
4150
4151 assert(session_url_is(ra_session,
4152 (is_rollback ? source->loc1 : source->loc2)->url,
4153 scratch_pool));
4154
4155 /* CHILDREN_WITH_MERGEINFO is sorted in depth-first order, so
4156 start at index 1 to examine only subtrees. */
4157 for (i = 1; i < children_with_mergeinfo->nelts; i++)
4158 {
4159 svn_client__merge_path_t *child =
4160 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
4161 svn_client__merge_path_t *parent;
4162 svn_rangelist_t *deleted_rangelist, *added_rangelist;
4163
4164 SVN_ERR_ASSERT(child);
4165 if (child->absent)
4166 continue;
4167
4168 svn_pool_clear(iterpool);
4169
4170 /* Find CHILD's parent. */
4171 parent = find_nearest_ancestor(children_with_mergeinfo,
4172 FALSE, child->abspath);
4173
4174 /* Since CHILD is a subtree then its parent must be in
4175 CHILDREN_WITH_MERGEINFO, see the global comment
4176 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
4177 SVN_ERR_ASSERT(parent);
4178
4179 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
4180 so it will work with the svn_rangelist_diff API. */
4181 if (is_rollback)
4182 {
4183 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
4184 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
4185 }
4186
4187 SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
4188 child->remaining_ranges,
4189 parent->remaining_ranges,
4190 TRUE, iterpool));
4191
4192 if (is_rollback)
4193 {
4194 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
4195 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
4196 }
4197
4198 /* If CHILD is the merge target we then know that SOURCE is provided
4199 by normalize_merge_sources() -- see 'MERGEINFO MERGE SOURCE
4200 NORMALIZATION'. Due to this normalization we know that SOURCE
4201 describes an unbroken line of history such that the entire range
4202 described by SOURCE can potentially be merged to CHILD.
4203
4204 But if CHILD is a subtree we don't have the same guarantees about
4205 SOURCE as we do for the merge target. SOURCE->loc1 and/or
4206 SOURCE->loc2 might not exist.
4207
4208 If one or both doesn't exist, then adjust CHILD->REMAINING_RANGES
4209 such that we don't later try to describe invalid subtrees in
4210 drive_merge_report_editor(), as that will break the merge.
4211 If CHILD has the same remaining ranges as PARENT however, then
4212 there is no need to make these adjustments, since
4213 drive_merge_report_editor() won't attempt to describe CHILD in this
4214 case, see the 'Note' in drive_merge_report_editor's docstring. */
4215 if (deleted_rangelist->nelts || added_rangelist->nelts)
4216 {
4217 const char *child_primary_source_url;
4218 const char *child_repos_src_path =
4219 svn_dirent_is_child(target->abspath, child->abspath, iterpool);
4220
4221 /* This loop is only processing subtrees, so CHILD->ABSPATH
4222 better be a proper child of the merge target. */
4223 SVN_ERR_ASSERT(child_repos_src_path);
4224
4225 child_primary_source_url =
4226 svn_path_url_add_component2((source->loc1->rev < source->loc2->rev)
4227 ? source->loc2->url : source->loc1->url,
4228 child_repos_src_path, iterpool);
4229
4230 SVN_ERR(adjust_deleted_subtree_ranges(child, parent,
4231 source->loc1->rev,
4232 source->loc2->rev,
4233 child_primary_source_url,
4234 ra_session,
4235 ctx, result_pool, iterpool));
4236 }
4237 }
4238
4239 svn_pool_destroy(iterpool);
4240 return SVN_NO_ERROR;
4241 }
4242
4243 /*-----------------------------------------------------------------------*/
4244
4245 /*** Determining What Remains To Be Merged ***/
4246
4247 /* Get explicit and/or implicit mergeinfo for the working copy path
4248 TARGET_ABSPATH.
4249
4250 If RECORDED_MERGEINFO is not NULL then set *RECORDED_MERGEINFO
4251 to TARGET_ABSPATH's explicit or inherited mergeinfo as dictated by
4252 INHERIT.
4253
4254 If IMPLICIT_MERGEINFO is not NULL then set *IMPLICIT_MERGEINFO
4255 to TARGET_ABSPATH's implicit mergeinfo (a.k.a. natural history).
4256
4257 If both RECORDED_MERGEINFO and IMPLICIT_MERGEINFO are not NULL and
4258 *RECORDED_MERGEINFO is inherited, then *IMPLICIT_MERGEINFO will be
4259 removed from *RECORDED_MERGEINFO.
4260
4261 If INHERITED is not NULL set *INHERITED to TRUE if *RECORDED_MERGEINFO
4262 is inherited rather than explicit. If RECORDED_MERGEINFO is NULL then
4263 INHERITED is ignored.
4264
4265
4266 If IMPLICIT_MERGEINFO is not NULL then START and END are limits on
4267 the natural history sought, must both be valid revision numbers, and
4268 START must be greater than END. If TARGET_ABSPATH's base revision
4269 is older than START, then the base revision is used as the younger
4270 bound in place of START.
4271
4272 RA_SESSION is an RA session open to the repository in which TARGET_ABSPATH
4273 lives. It may be temporarily reparented as needed by this function.
4274
4275 Allocate *RECORDED_MERGEINFO and *IMPLICIT_MERGEINFO in RESULT_POOL.
4276 Use SCRATCH_POOL for any temporary allocations. */
4277 static svn_error_t *
get_full_mergeinfo(svn_mergeinfo_t * recorded_mergeinfo,svn_mergeinfo_t * implicit_mergeinfo,svn_boolean_t * inherited,svn_mergeinfo_inheritance_t inherit,svn_ra_session_t * ra_session,const char * target_abspath,svn_revnum_t start,svn_revnum_t end,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4278 get_full_mergeinfo(svn_mergeinfo_t *recorded_mergeinfo,
4279 svn_mergeinfo_t *implicit_mergeinfo,
4280 svn_boolean_t *inherited,
4281 svn_mergeinfo_inheritance_t inherit,
4282 svn_ra_session_t *ra_session,
4283 const char *target_abspath,
4284 svn_revnum_t start,
4285 svn_revnum_t end,
4286 svn_client_ctx_t *ctx,
4287 apr_pool_t *result_pool,
4288 apr_pool_t *scratch_pool)
4289 {
4290 /* First, we get the real mergeinfo. */
4291 if (recorded_mergeinfo)
4292 {
4293 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(recorded_mergeinfo,
4294 inherited,
4295 NULL /* from_repos */,
4296 FALSE,
4297 inherit, ra_session,
4298 target_abspath,
4299 ctx, result_pool));
4300 }
4301
4302 if (implicit_mergeinfo)
4303 {
4304 svn_client__pathrev_t *target;
4305
4306 /* Assert that we have sane input. */
4307 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start) && SVN_IS_VALID_REVNUM(end)
4308 && (start > end));
4309
4310 /* Retrieve the origin (original_*) of the node, or just the
4311 url if the node was not copied. */
4312 SVN_ERR(svn_client__wc_node_get_origin(&target, target_abspath, ctx,
4313 scratch_pool, scratch_pool));
4314
4315 if (! target)
4316 {
4317 /* We've been asked to operate on a locally added target, so its
4318 * implicit mergeinfo is empty. */
4319 *implicit_mergeinfo = apr_hash_make(result_pool);
4320 }
4321 else if (target->rev <= end)
4322 {
4323 /* We're asking about a range outside our natural history
4324 altogether. That means our implicit mergeinfo is empty. */
4325 *implicit_mergeinfo = apr_hash_make(result_pool);
4326 }
4327 else
4328 {
4329 /* Fetch so-called "implicit mergeinfo" (that is, natural
4330 history). */
4331
4332 /* Do not ask for implicit mergeinfo from TARGET_ABSPATH's future.
4333 TARGET_ABSPATH might not even exist, and even if it does the
4334 working copy is *at* TARGET_REV so its implicit history ends
4335 at TARGET_REV! */
4336 if (target->rev < start)
4337 start = target->rev;
4338
4339 /* Fetch the implicit mergeinfo. */
4340 SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo,
4341 NULL,
4342 target, start, end,
4343 ra_session, ctx,
4344 result_pool));
4345 }
4346 } /*if (implicit_mergeinfo) */
4347
4348 return SVN_NO_ERROR;
4349 }
4350
4351 /* Helper for ensure_implicit_mergeinfo().
4352
4353 PARENT, CHILD, REVISION1, REVISION2 and CTX
4354 are all cascaded from the arguments of the same names in
4355 ensure_implicit_mergeinfo(). PARENT and CHILD must both exist, i.e.
4356 this function should never be called where CHILD is the merge target.
4357
4358 If PARENT->IMPLICIT_MERGEINFO is NULL, obtain it from the server.
4359
4360 Set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
4361 PARENT->IMPLICIT_MERGEINFO. CHILD->IMPLICIT_MERGEINFO is allocated
4362 in RESULT_POOL.
4363
4364 RA_SESSION is an RA session open to the repository that contains CHILD.
4365 It may be temporarily reparented by this function.
4366 */
4367 static svn_error_t *
inherit_implicit_mergeinfo_from_parent(svn_client__merge_path_t * parent,svn_client__merge_path_t * child,svn_revnum_t revision1,svn_revnum_t revision2,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4368 inherit_implicit_mergeinfo_from_parent(svn_client__merge_path_t *parent,
4369 svn_client__merge_path_t *child,
4370 svn_revnum_t revision1,
4371 svn_revnum_t revision2,
4372 svn_ra_session_t *ra_session,
4373 svn_client_ctx_t *ctx,
4374 apr_pool_t *result_pool,
4375 apr_pool_t *scratch_pool)
4376 {
4377 const char *path_diff;
4378
4379 /* This only works on subtrees! */
4380 SVN_ERR_ASSERT(parent);
4381 SVN_ERR_ASSERT(child);
4382
4383 /* While PARENT must exist, it is possible we've deferred
4384 getting its implicit mergeinfo. If so get it now. */
4385 if (!parent->implicit_mergeinfo)
4386 SVN_ERR(get_full_mergeinfo(NULL, &(parent->implicit_mergeinfo),
4387 NULL, svn_mergeinfo_inherited,
4388 ra_session, child->abspath,
4389 MAX(revision1, revision2),
4390 MIN(revision1, revision2),
4391 ctx, result_pool, scratch_pool));
4392
4393 /* Let CHILD inherit PARENT's implicit mergeinfo. */
4394
4395 path_diff = svn_dirent_is_child(parent->abspath, child->abspath,
4396 scratch_pool);
4397 /* PARENT->PATH better be an ancestor of CHILD->ABSPATH! */
4398 SVN_ERR_ASSERT(path_diff);
4399
4400 SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
4401 &child->implicit_mergeinfo, parent->implicit_mergeinfo,
4402 path_diff, result_pool, scratch_pool));
4403 child->implicit_mergeinfo = svn_mergeinfo_dup(child->implicit_mergeinfo,
4404 result_pool);
4405 return SVN_NO_ERROR;
4406 }
4407
4408 /* Helper of filter_merged_revisions().
4409
4410 If we have deferred obtaining CHILD->IMPLICIT_MERGEINFO, then get
4411 it now, allocating it in RESULT_POOL. If CHILD_INHERITS_PARENT is true
4412 then set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
4413 PARENT->IMPLICIT_MERGEINFO, otherwise contact the repository. Use
4414 SCRATCH_POOL for all temporary allocations.
4415
4416 RA_SESSION is an RA session open to the repository that contains CHILD.
4417 It may be temporarily reparented by this function.
4418
4419 PARENT, CHILD, REVISION1, REVISION2 and
4420 CTX are all cascaded from the arguments of the same name in
4421 filter_merged_revisions() and the same conditions for that function
4422 hold here. */
4423 static svn_error_t *
ensure_implicit_mergeinfo(svn_client__merge_path_t * parent,svn_client__merge_path_t * child,svn_boolean_t child_inherits_parent,svn_revnum_t revision1,svn_revnum_t revision2,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4424 ensure_implicit_mergeinfo(svn_client__merge_path_t *parent,
4425 svn_client__merge_path_t *child,
4426 svn_boolean_t child_inherits_parent,
4427 svn_revnum_t revision1,
4428 svn_revnum_t revision2,
4429 svn_ra_session_t *ra_session,
4430 svn_client_ctx_t *ctx,
4431 apr_pool_t *result_pool,
4432 apr_pool_t *scratch_pool)
4433 {
4434 /* If we haven't already found CHILD->IMPLICIT_MERGEINFO then
4435 contact the server to get it. */
4436
4437 if (child->implicit_mergeinfo)
4438 return SVN_NO_ERROR;
4439
4440 if (child_inherits_parent)
4441 SVN_ERR(inherit_implicit_mergeinfo_from_parent(parent,
4442 child,
4443 revision1,
4444 revision2,
4445 ra_session,
4446 ctx,
4447 result_pool,
4448 scratch_pool));
4449 else
4450 SVN_ERR(get_full_mergeinfo(NULL,
4451 &(child->implicit_mergeinfo),
4452 NULL, svn_mergeinfo_inherited,
4453 ra_session, child->abspath,
4454 MAX(revision1, revision2),
4455 MIN(revision1, revision2),
4456 ctx, result_pool, scratch_pool));
4457
4458 return SVN_NO_ERROR;
4459 }
4460
4461 /* Helper for calculate_remaining_ranges().
4462
4463 Initialize CHILD->REMAINING_RANGES to a rangelist representing the
4464 requested merge of REVISION1:REVISION2 from MERGEINFO_PATH to
4465 CHILD->ABSPATH.
4466
4467 For forward merges remove any ranges from CHILD->REMAINING_RANGES that
4468 have already been merged to CHILD->ABSPATH per TARGET_MERGEINFO or
4469 CHILD->IMPLICIT_MERGEINFO. For reverse merges remove any ranges from
4470 CHILD->REMAINING_RANGES that have not already been merged to CHILD->ABSPATH
4471 per TARGET_MERGEINFO or CHILD->IMPLICIT_MERGEINFO. If we have deferred
4472 obtaining CHILD->IMPLICIT_MERGEINFO and it is necessary to use it for
4473 these calculations, then get it from the server, allocating it in
4474 RESULT_POOL.
4475
4476 CHILD represents a working copy path which is the merge target or one of
4477 the target's subtrees. If not NULL, PARENT is CHILD's nearest path-wise
4478 ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'.
4479
4480 If the function needs to consider CHILD->IMPLICIT_MERGEINFO and
4481 CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the
4482 mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO. Otherwise contact
4483 the repository for CHILD->IMPLICIT_MERGEINFO.
4484
4485 NOTE: If PARENT is present then this function must have previously been
4486 called for PARENT, i.e. if populate_remaining_ranges() is calling this
4487 function for a set of svn_client__merge_path_t* the calls must be made
4488 in depth-first order.
4489
4490 MERGEINFO_PATH is the merge source relative to the repository root.
4491
4492 REVISION1 and REVISION2 describe the merge range requested from
4493 MERGEINFO_PATH.
4494
4495 TARGET_RANGELIST is the portion of CHILD->ABSPATH's explicit or inherited
4496 mergeinfo that intersects with the merge history described by
4497 MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2. TARGET_RANGELIST
4498 should be NULL if there is no explicit or inherited mergeinfo on
4499 CHILD->ABSPATH or an empty list if CHILD->ABSPATH has empty mergeinfo or
4500 explicit mergeinfo that exclusively describes non-intersecting history
4501 with MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2.
4502
4503 SCRATCH_POOL is used for all temporary allocations.
4504
4505 NOTE: This should only be called when honoring mergeinfo.
4506
4507 NOTE: Like calculate_remaining_ranges() if PARENT is present then this
4508 function must have previously been called for PARENT.
4509 */
4510 static svn_error_t *
filter_merged_revisions(svn_client__merge_path_t * parent,svn_client__merge_path_t * child,const char * mergeinfo_path,svn_rangelist_t * target_rangelist,svn_revnum_t revision1,svn_revnum_t revision2,svn_boolean_t child_inherits_implicit,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4511 filter_merged_revisions(svn_client__merge_path_t *parent,
4512 svn_client__merge_path_t *child,
4513 const char *mergeinfo_path,
4514 svn_rangelist_t *target_rangelist,
4515 svn_revnum_t revision1,
4516 svn_revnum_t revision2,
4517 svn_boolean_t child_inherits_implicit,
4518 svn_ra_session_t *ra_session,
4519 svn_client_ctx_t *ctx,
4520 apr_pool_t *result_pool,
4521 apr_pool_t *scratch_pool)
4522 {
4523 svn_rangelist_t *requested_rangelist,
4524 *target_implicit_rangelist, *explicit_rangelist;
4525
4526 /* Convert REVISION1 and REVISION2 to a rangelist.
4527
4528 Note: Talking about a requested merge range's inheritability
4529 doesn't make much sense, but as we are using svn_merge_range_t
4530 to describe it we need to pick *something*. Since all the
4531 rangelist manipulations in this function either don't consider
4532 inheritance by default or we are requesting that they don't (i.e.
4533 svn_rangelist_remove and svn_rangelist_intersect) then we could
4534 set the inheritability as FALSE, it won't matter either way. */
4535 requested_rangelist = svn_rangelist__initialize(revision1, revision2,
4536 TRUE, scratch_pool);
4537
4538 /* Now filter out revisions that have already been merged to CHILD. */
4539
4540 if (revision1 > revision2) /* This is a reverse merge. */
4541 {
4542 svn_rangelist_t *added_rangelist, *deleted_rangelist;
4543
4544 /* The revert range and will need to be reversed for
4545 our svn_rangelist_* APIs to work properly. */
4546 SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
4547
4548 /* Set EXPLICIT_RANGELIST to the list of source-range revs that are
4549 already recorded as merged to target. */
4550 if (target_rangelist)
4551 {
4552 /* Return the intersection of the revs which are both already
4553 represented by CHILD's explicit or inherited mergeinfo.
4554
4555 We don't consider inheritance when determining intersecting
4556 ranges. If we *did* consider inheritance, then our calculation
4557 would be wrong. For example, if the CHILD->REMAINING_RANGES is
4558 5:3 and TARGET_RANGELIST is r5* (non-inheritable) then the
4559 intersection would be r4. And that would be wrong as we clearly
4560 want to reverse merge both r4 and r5 in this case. Ignoring the
4561 ranges' inheritance results in an intersection of r4-5.
4562
4563 You might be wondering about CHILD's children, doesn't the above
4564 imply that we will reverse merge r4-5 from them? Nope, this is
4565 safe to do because any path whose parent has non-inheritable
4566 ranges is always considered a subtree with differing mergeinfo
4567 even if that path has no explicit mergeinfo prior to the
4568 merge -- See condition 3 in the doc string for
4569 merge.c:get_mergeinfo_paths(). */
4570 SVN_ERR(svn_rangelist_intersect(&explicit_rangelist,
4571 target_rangelist,
4572 requested_rangelist,
4573 FALSE, scratch_pool));
4574 }
4575 else
4576 {
4577 explicit_rangelist =
4578 apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *));
4579 }
4580
4581 /* Was any part of the requested reverse merge not accounted for in
4582 CHILD's explicit or inherited mergeinfo? */
4583 SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
4584 requested_rangelist, explicit_rangelist,
4585 FALSE, scratch_pool));
4586
4587 if (deleted_rangelist->nelts == 0)
4588 {
4589 /* The whole of REVISION1:REVISION2 was represented in CHILD's
4590 explicit/inherited mergeinfo, allocate CHILD's remaining
4591 ranges in POOL and then we are done. */
4592 SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
4593 child->remaining_ranges = svn_rangelist_dup(requested_rangelist,
4594 result_pool);
4595 }
4596 else /* We need to check CHILD's implicit mergeinfo. */
4597 {
4598 svn_rangelist_t *implicit_rangelist;
4599
4600 SVN_ERR(ensure_implicit_mergeinfo(parent,
4601 child,
4602 child_inherits_implicit,
4603 revision1,
4604 revision2,
4605 ra_session,
4606 ctx,
4607 result_pool,
4608 scratch_pool));
4609
4610 target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo,
4611 mergeinfo_path);
4612
4613 if (target_implicit_rangelist)
4614 SVN_ERR(svn_rangelist_intersect(&implicit_rangelist,
4615 target_implicit_rangelist,
4616 requested_rangelist,
4617 FALSE, scratch_pool));
4618 else
4619 implicit_rangelist = apr_array_make(scratch_pool, 0,
4620 sizeof(svn_merge_range_t *));
4621
4622 SVN_ERR(svn_rangelist_merge2(implicit_rangelist,
4623 explicit_rangelist, scratch_pool,
4624 scratch_pool));
4625 SVN_ERR(svn_rangelist_reverse(implicit_rangelist, scratch_pool));
4626 child->remaining_ranges = svn_rangelist_dup(implicit_rangelist,
4627 result_pool);
4628 }
4629 }
4630 else /* This is a forward merge */
4631 {
4632 /* Set EXPLICIT_RANGELIST to the list of source-range revs that are
4633 NOT already recorded as merged to target. */
4634 if (target_rangelist)
4635 {
4636 /* See earlier comment preceding svn_rangelist_intersect() for
4637 why we don't consider inheritance here. */
4638 SVN_ERR(svn_rangelist_remove(&explicit_rangelist,
4639 target_rangelist,
4640 requested_rangelist, FALSE,
4641 scratch_pool));
4642 }
4643 else
4644 {
4645 explicit_rangelist = svn_rangelist_dup(requested_rangelist,
4646 scratch_pool);
4647 }
4648
4649 if (explicit_rangelist->nelts == 0)
4650 {
4651 child->remaining_ranges =
4652 apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *));
4653 }
4654 else
4655 /* ### TODO: Which evil shall we choose?
4656 ###
4657 ### If we allow all forward-merges not already found in recorded
4658 ### mergeinfo, we destroy the ability to, say, merge the whole of a
4659 ### branch to the trunk while automatically ignoring the revisions
4660 ### common to both. That's bad.
4661 ###
4662 ### If we allow only forward-merges not found in either recorded
4663 ### mergeinfo or implicit mergeinfo (natural history), then the
4664 ### previous scenario works great, but we can't reverse-merge a
4665 ### previous change made to our line of history and then remake it
4666 ### (because the reverse-merge will leave no mergeinfo trace, and
4667 ### the remake-it attempt will still find the original change in
4668 ### natural mergeinfo. But you know, that we happen to use 'merge'
4669 ### for revision undoing is somewhat unnatural anyway, so I'm
4670 ### finding myself having little interest in caring too much about
4671 ### this. That said, if we had a way of storing reverse merge
4672 ### ranges, we'd be in good shape either way.
4673 */
4674 #ifdef SVN_MERGE__ALLOW_ALL_FORWARD_MERGES_FROM_SELF
4675 {
4676 /* ### Don't consider implicit mergeinfo. */
4677 child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
4678 pool);
4679 }
4680 #else
4681 {
4682 /* Based on CHILD's TARGET_MERGEINFO there are ranges to merge.
4683 Check CHILD's implicit mergeinfo to see if these remaining
4684 ranges are represented there. */
4685 SVN_ERR(ensure_implicit_mergeinfo(parent,
4686 child,
4687 child_inherits_implicit,
4688 revision1,
4689 revision2,
4690 ra_session,
4691 ctx,
4692 result_pool,
4693 scratch_pool));
4694
4695 target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo,
4696 mergeinfo_path);
4697 if (target_implicit_rangelist)
4698 SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
4699 target_implicit_rangelist,
4700 explicit_rangelist,
4701 FALSE, result_pool));
4702 else
4703 child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
4704 result_pool);
4705 }
4706 #endif
4707 }
4708
4709 return SVN_NO_ERROR;
4710 }
4711
4712 /* Helper for do_file_merge and do_directory_merge (by way of
4713 populate_remaining_ranges() for the latter).
4714
4715 Determine what portions of SOURCE have already
4716 been merged to CHILD->ABSPATH and populate CHILD->REMAINING_RANGES with
4717 the ranges that still need merging.
4718
4719 SOURCE and CTX are all cascaded from the caller's arguments of the same
4720 names. Note that this means SOURCE adheres to the requirements noted in
4721 `MERGEINFO MERGE SOURCE NORMALIZATION'.
4722
4723 CHILD represents a working copy path which is the merge target or one of
4724 the target's subtrees. If not NULL, PARENT is CHILD's nearest path-wise
4725 ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. TARGET_MERGEINFO is
4726 the working mergeinfo on CHILD.
4727
4728 RA_SESSION is the session for the younger of SOURCE->loc1 and
4729 SOURCE->loc2.
4730
4731 If the function needs to consider CHILD->IMPLICIT_MERGEINFO and
4732 CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the
4733 mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO. Otherwise contact
4734 the repository for CHILD->IMPLICIT_MERGEINFO.
4735
4736 If not null, IMPLICIT_SRC_GAP is the gap, if any, in the natural history
4737 of SOURCE, see merge_cmd_baton_t.implicit_src_gap.
4738
4739 SCRATCH_POOL is used for all temporary allocations. Changes to CHILD and
4740 PARENT are made in RESULT_POOL.
4741
4742 NOTE: This should only be called when honoring mergeinfo.
4743
4744 NOTE: If PARENT is present then this function must have previously been
4745 called for PARENT, i.e. if populate_remaining_ranges() is calling this
4746 function for a set of svn_client__merge_path_t* the calls must be made
4747 in depth-first order.
4748
4749 NOTE: When performing reverse merges, return
4750 SVN_ERR_CLIENT_NOT_READY_TO_MERGE if both locations in SOURCE and
4751 CHILD->ABSPATH are all on the same line of history but CHILD->ABSPATH's
4752 base revision is older than the SOURCE->rev1:rev2 range, see comment re
4753 issue #2973 below.
4754 */
4755 static svn_error_t *
calculate_remaining_ranges(svn_client__merge_path_t * parent,svn_client__merge_path_t * child,const merge_source_t * source,svn_mergeinfo_t target_mergeinfo,const apr_array_header_t * implicit_src_gap,svn_boolean_t child_inherits_implicit,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4756 calculate_remaining_ranges(svn_client__merge_path_t *parent,
4757 svn_client__merge_path_t *child,
4758 const merge_source_t *source,
4759 svn_mergeinfo_t target_mergeinfo,
4760 const apr_array_header_t *implicit_src_gap,
4761 svn_boolean_t child_inherits_implicit,
4762 svn_ra_session_t *ra_session,
4763 svn_client_ctx_t *ctx,
4764 apr_pool_t *result_pool,
4765 apr_pool_t *scratch_pool)
4766 {
4767 const svn_client__pathrev_t *primary_src
4768 = (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1;
4769 const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
4770 scratch_pool);
4771 /* Intersection of TARGET_MERGEINFO and the merge history
4772 described by SOURCE. */
4773 svn_rangelist_t *target_rangelist;
4774 svn_revnum_t child_base_revision;
4775
4776 /* Since this function should only be called when honoring mergeinfo and
4777 * SOURCE adheres to the requirements noted in 'MERGEINFO MERGE SOURCE
4778 * NORMALIZATION', SOURCE must be 'ancestral'. */
4779 SVN_ERR_ASSERT(source->ancestral);
4780
4781 /* Determine which of the requested ranges to consider merging... */
4782
4783 /* Set TARGET_RANGELIST to the portion of TARGET_MERGEINFO that refers
4784 to SOURCE (excluding any gap in SOURCE): first get all ranges from
4785 TARGET_MERGEINFO that refer to the path of SOURCE, and then prune
4786 any ranges that lie in the gap in SOURCE.
4787
4788 ### [JAF] In fact, that may still leave some ranges that lie entirely
4789 outside the range of SOURCE; it seems we don't care about that. */
4790 if (target_mergeinfo)
4791 target_rangelist = svn_hash_gets(target_mergeinfo, mergeinfo_path);
4792 else
4793 target_rangelist = NULL;
4794 if (implicit_src_gap && target_rangelist)
4795 {
4796 /* Remove any mergeinfo referring to the 'gap' in SOURCE, as that
4797 mergeinfo doesn't really refer to SOURCE at all but instead
4798 refers to locations that are non-existent or on a different
4799 line of history. (Issue #3242.) */
4800 SVN_ERR(svn_rangelist_remove(&target_rangelist,
4801 implicit_src_gap, target_rangelist,
4802 FALSE, result_pool));
4803 }
4804
4805 /* Initialize CHILD->REMAINING_RANGES and filter out revisions already
4806 merged (or, in the case of reverse merges, ranges not yet merged). */
4807 SVN_ERR(filter_merged_revisions(parent, child, mergeinfo_path,
4808 target_rangelist,
4809 source->loc1->rev, source->loc2->rev,
4810 child_inherits_implicit,
4811 ra_session, ctx, result_pool,
4812 scratch_pool));
4813
4814 /* Issue #2973 -- from the continuing series of "Why, since the advent of
4815 merge tracking, allowing merges into mixed rev and locally modified
4816 working copies isn't simple and could be considered downright evil".
4817
4818 If reverse merging a range to the WC path represented by CHILD, from
4819 that path's own history, where the path inherits no locally modified
4820 mergeinfo from its WC parents (i.e. there is no uncommitted merge to
4821 the WC), and the path's base revision is older than the range, then
4822 the merge will always be a no-op. This is because we only allow reverse
4823 merges of ranges in the path's explicit or natural mergeinfo and a
4824 reverse merge from the path's future history obviously isn't going to be
4825 in either, hence the no-op.
4826
4827 The problem is two-fold. First, in a mixed rev WC, the change we
4828 want to revert might actually be to some child of the target path
4829 which is at a younger base revision. Sure, we can merge directly
4830 to that child or update the WC or even use --ignore-ancestry and then
4831 successfully run the reverse merge, but that gets to the second
4832 problem: Those courses of action are not very obvious. Before 1.5 if
4833 a user committed a change that didn't touch the commit target, then
4834 immediately decided to revert that change via a reverse merge it would
4835 just DTRT. But with the advent of merge tracking the user gets a no-op.
4836
4837 So in the name of user friendliness, return an error suggesting a helpful
4838 course of action.
4839 */
4840 SVN_ERR(svn_wc__node_get_base(NULL, &child_base_revision,
4841 NULL, NULL, NULL, NULL,
4842 ctx->wc_ctx, child->abspath,
4843 TRUE /* ignore_enoent */,
4844 scratch_pool, scratch_pool));
4845 /* If CHILD has no base revision then it hasn't been committed yet, so it
4846 can't have any "future" history. */
4847 if (SVN_IS_VALID_REVNUM(child_base_revision)
4848 && ((child->remaining_ranges)->nelts == 0) /* Inoperative merge */
4849 && (source->loc2->rev < source->loc1->rev) /* Reverse merge */
4850 && (child_base_revision <= source->loc2->rev)) /* From CHILD's future */
4851 {
4852 /* Hmmm, an inoperative reverse merge from the "future". If it is
4853 from our own future return a helpful error. */
4854 svn_error_t *err;
4855 svn_client__pathrev_t *start_loc;
4856
4857 err = svn_client__repos_location(&start_loc,
4858 ra_session,
4859 source->loc1,
4860 child_base_revision,
4861 ctx, scratch_pool, scratch_pool);
4862 if (err)
4863 {
4864 if (err->apr_err == SVN_ERR_FS_NOT_FOUND
4865 || err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
4866 svn_error_clear(err);
4867 else
4868 return svn_error_trace(err);
4869 }
4870 else
4871 {
4872 const char *url;
4873
4874 SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, child->abspath,
4875 scratch_pool, scratch_pool));
4876 if (strcmp(start_loc->url, url) == 0)
4877 return svn_error_create(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
4878 _("Cannot reverse-merge a range from a "
4879 "path's own future history; try "
4880 "updating first"));
4881 }
4882 }
4883
4884 return SVN_NO_ERROR;
4885 }
4886
4887 /* Helper for populate_remaining_ranges().
4888
4889 SOURCE is cascaded from the arguments of the same name in
4890 populate_remaining_ranges().
4891
4892 Note: The following comments assume a forward merge, i.e.
4893 SOURCE->loc1->rev < SOURCE->loc2->rev. If this is a reverse merge then
4894 all the following comments still apply, but with SOURCE->loc1 switched
4895 with SOURCE->loc2.
4896
4897 Like populate_remaining_ranges(), SOURCE must adhere to the restrictions
4898 documented in 'MERGEINFO MERGE SOURCE NORMALIZATION'. These restrictions
4899 allow for a *single* gap in SOURCE, GAP_REV1:GAP_REV2 exclusive:inclusive
4900 (where SOURCE->loc1->rev == GAP_REV1 <= GAP_REV2 < SOURCE->loc2->rev),
4901 if SOURCE->loc2->url@(GAP_REV2+1) was copied from SOURCE->loc1. If such
4902 a gap exists, set *GAP_START and *GAP_END to the starting and ending
4903 revisions of the gap. Otherwise set both to SVN_INVALID_REVNUM.
4904
4905 For example, if the natural history of URL@2:URL@9 is 'trunk/:2,7-9' this
4906 would indicate that trunk@7 was copied from trunk@2. This function would
4907 return GAP_START:GAP_END of 2:6 in this case. Note that a path 'trunk'
4908 might exist at r3-6, but it would not be on the same line of history as
4909 trunk@9.
4910
4911 ### GAP_START is basically redundant, as (if there is a gap at all) it is
4912 necessarily the older revision of SOURCE.
4913
4914 RA_SESSION is an open RA session to the repository in which SOURCE lives.
4915 */
4916 static svn_error_t *
find_gaps_in_merge_source_history(svn_revnum_t * gap_start,svn_revnum_t * gap_end,const merge_source_t * source,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * scratch_pool)4917 find_gaps_in_merge_source_history(svn_revnum_t *gap_start,
4918 svn_revnum_t *gap_end,
4919 const merge_source_t *source,
4920 svn_ra_session_t *ra_session,
4921 svn_client_ctx_t *ctx,
4922 apr_pool_t *scratch_pool)
4923 {
4924 svn_mergeinfo_t implicit_src_mergeinfo;
4925 svn_revnum_t old_rev = MIN(source->loc1->rev, source->loc2->rev);
4926 const svn_client__pathrev_t *primary_src
4927 = (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1;
4928 const char *merge_src_fspath = svn_client__pathrev_fspath(primary_src,
4929 scratch_pool);
4930 svn_rangelist_t *rangelist;
4931
4932 SVN_ERR_ASSERT(source->ancestral);
4933
4934 /* Start by assuming there is no gap. */
4935 *gap_start = *gap_end = SVN_INVALID_REVNUM;
4936
4937 /* Easy out: There can't be a gap between adjacent revisions. */
4938 if (labs(source->loc1->rev - source->loc2->rev) == 1)
4939 return SVN_NO_ERROR;
4940
4941 /* Get SOURCE as mergeinfo. */
4942 SVN_ERR(svn_client__get_history_as_mergeinfo(&implicit_src_mergeinfo, NULL,
4943 primary_src,
4944 primary_src->rev, old_rev,
4945 ra_session,
4946 ctx, scratch_pool));
4947
4948 rangelist = svn_hash_gets(implicit_src_mergeinfo, merge_src_fspath);
4949
4950 if (!rangelist) /* ### Can we ever not find a rangelist? */
4951 return SVN_NO_ERROR;
4952
4953 /* A gap in natural history can result from either a copy or
4954 a rename. If from a copy then history as mergeinfo will look
4955 something like this:
4956
4957 '/trunk:X,Y-Z'
4958
4959 If from a rename it will look like this:
4960
4961 '/trunk_old_name:X'
4962 '/trunk_new_name:Y-Z'
4963
4964 In both cases the gap, if it exists, is M-N, where M = X + 1 and
4965 N = Y - 1.
4966
4967 Note that per the rules of 'MERGEINFO MERGE SOURCE NORMALIZATION' we
4968 should never have multiple gaps, e.g. if we see anything like the
4969 following then something is quite wrong:
4970
4971 '/trunk_old_name:A,B-C'
4972 '/trunk_new_name:D-E'
4973 */
4974
4975 if (rangelist->nelts > 1) /* Copy */
4976 {
4977 const svn_merge_range_t *gap;
4978 /* As mentioned above, multiple gaps *shouldn't* be possible. */
4979 SVN_ERR_ASSERT(apr_hash_count(implicit_src_mergeinfo) == 1);
4980
4981 gap = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
4982 const svn_merge_range_t *);
4983
4984 *gap_start = MIN(source->loc1->rev, source->loc2->rev);
4985 *gap_end = gap->start;
4986
4987 /* ### Issue #4132:
4988 ### This assertion triggers in merge_tests.py svnmucc_abuse_1()
4989 ### when a node is replaced by an older copy of itself.
4990
4991 BH: I think we should review this and the 'rename' case to find
4992 out which behavior we really want, and if we can really
4993 determine what happened this way. */
4994 SVN_ERR_ASSERT(*gap_start < *gap_end);
4995 }
4996 else if (apr_hash_count(implicit_src_mergeinfo) > 1) /* Rename */
4997 {
4998 svn_rangelist_t *requested_rangelist =
4999 svn_rangelist__initialize(MIN(source->loc1->rev, source->loc2->rev),
5000 MAX(source->loc1->rev, source->loc2->rev),
5001 TRUE, scratch_pool);
5002 svn_rangelist_t *implicit_rangelist =
5003 apr_array_make(scratch_pool, 2, sizeof(svn_merge_range_t *));
5004 svn_rangelist_t *gap_rangelist;
5005
5006 SVN_ERR(svn_rangelist__merge_many(implicit_rangelist,
5007 implicit_src_mergeinfo,
5008 scratch_pool, scratch_pool));
5009 SVN_ERR(svn_rangelist_remove(&gap_rangelist, implicit_rangelist,
5010 requested_rangelist, FALSE,
5011 scratch_pool));
5012
5013 /* If there is anything left it is the gap. */
5014 if (gap_rangelist->nelts)
5015 {
5016 svn_merge_range_t *gap_range =
5017 APR_ARRAY_IDX(gap_rangelist, 0, svn_merge_range_t *);
5018
5019 *gap_start = gap_range->start;
5020 *gap_end = gap_range->end;
5021 }
5022 }
5023
5024 SVN_ERR_ASSERT(*gap_start == MIN(source->loc1->rev, source->loc2->rev)
5025 || (*gap_start == SVN_INVALID_REVNUM
5026 && *gap_end == SVN_INVALID_REVNUM));
5027 return SVN_NO_ERROR;
5028 }
5029
5030 /* Helper for do_directory_merge().
5031
5032 For each (svn_client__merge_path_t *) child in CHILDREN_WITH_MERGEINFO,
5033 populate that child's 'remaining_ranges' list with (### ... what?),
5034 and populate that child's 'implicit_mergeinfo' with its implicit
5035 mergeinfo (natural history). CHILDREN_WITH_MERGEINFO is expected
5036 to be sorted in depth first order and each child must be processed in
5037 that order. The inheritability of all calculated ranges is TRUE.
5038
5039 If mergeinfo is being honored (based on MERGE_B -- see HONOR_MERGEINFO()
5040 for how this is determined), this function will actually try to be
5041 intelligent about populating remaining_ranges list. Otherwise, it
5042 will claim that each child has a single remaining range, from
5043 SOURCE->rev1, to SOURCE->rev2.
5044 ### We also take the short-cut if doing record-only. Why?
5045
5046 SCRATCH_POOL is used for all temporary allocations. Changes to
5047 CHILDREN_WITH_MERGEINFO are made in RESULT_POOL.
5048
5049 Note that if SOURCE->rev1 > SOURCE->rev2, then each child's remaining_ranges
5050 member does not adhere to the API rules for rangelists described in
5051 svn_mergeinfo.h -- See svn_client__merge_path_t.
5052
5053 See `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements
5054 around SOURCE.
5055 */
5056 static svn_error_t *
populate_remaining_ranges(apr_array_header_t * children_with_mergeinfo,const merge_source_t * source,svn_ra_session_t * ra_session,merge_cmd_baton_t * merge_b,apr_pool_t * result_pool,apr_pool_t * scratch_pool)5057 populate_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
5058 const merge_source_t *source,
5059 svn_ra_session_t *ra_session,
5060 merge_cmd_baton_t *merge_b,
5061 apr_pool_t *result_pool,
5062 apr_pool_t *scratch_pool)
5063 {
5064 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5065 int i;
5066 svn_revnum_t gap_start, gap_end;
5067
5068 /* If we aren't honoring mergeinfo or this is a --record-only merge,
5069 we'll make quick work of this by simply adding dummy SOURCE->rev1:rev2
5070 ranges for all children. */
5071 if (! HONOR_MERGEINFO(merge_b) || merge_b->record_only)
5072 {
5073 for (i = 0; i < children_with_mergeinfo->nelts; i++)
5074 {
5075 svn_client__merge_path_t *child =
5076 APR_ARRAY_IDX(children_with_mergeinfo, i,
5077 svn_client__merge_path_t *);
5078
5079 svn_pool_clear(iterpool);
5080
5081 /* Issue #3646 'record-only merges create self-referential
5082 mergeinfo'. Get the merge target's implicit mergeinfo (natural
5083 history). We'll use it later to avoid setting self-referential
5084 mergeinfo -- see filter_natural_history_from_mergeinfo(). */
5085 if (i == 0) /* First item is always the merge target. */
5086 {
5087 SVN_ERR(get_full_mergeinfo(NULL, /* child->pre_merge_mergeinfo */
5088 &(child->implicit_mergeinfo),
5089 NULL, /* child->inherited_mergeinfo */
5090 svn_mergeinfo_inherited, ra_session,
5091 child->abspath,
5092 MAX(source->loc1->rev,
5093 source->loc2->rev),
5094 MIN(source->loc1->rev,
5095 source->loc2->rev),
5096 merge_b->ctx, result_pool,
5097 iterpool));
5098 }
5099 else
5100 {
5101 /* Issue #3443 - Subtrees of the merge target can inherit
5102 their parent's implicit mergeinfo in most cases. */
5103 svn_client__merge_path_t *parent
5104 = find_nearest_ancestor(children_with_mergeinfo,
5105 FALSE, child->abspath);
5106 svn_boolean_t child_inherits_implicit;
5107
5108 /* If CHILD is a subtree then its parent must be in
5109 CHILDREN_WITH_MERGEINFO, see the global comment
5110 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
5111 SVN_ERR_ASSERT(parent);
5112
5113 child_inherits_implicit = (parent && !child->switched);
5114 SVN_ERR(ensure_implicit_mergeinfo(parent, child,
5115 child_inherits_implicit,
5116 source->loc1->rev,
5117 source->loc2->rev,
5118 ra_session, merge_b->ctx,
5119 result_pool, iterpool));
5120 }
5121
5122 child->remaining_ranges = svn_rangelist__initialize(source->loc1->rev,
5123 source->loc2->rev,
5124 TRUE,
5125 result_pool);
5126 }
5127 svn_pool_destroy(iterpool);
5128 return SVN_NO_ERROR;
5129 }
5130
5131 /* If, in the merge source's history, there was a copy from an older
5132 revision, then SOURCE->loc2->url won't exist at some range M:N, where
5133 SOURCE->loc1->rev < M < N < SOURCE->loc2->rev. The rules of 'MERGEINFO
5134 MERGE SOURCE NORMALIZATION' allow this, but we must ignore these gaps
5135 when calculating what ranges remain to be merged from SOURCE. If we
5136 don't and try to merge any part of SOURCE->loc2->url@M:N we would
5137 break the editor since no part of that actually exists. See
5138 http://svn.haxx.se/dev/archive-2008-11/0618.shtml.
5139
5140 Find the gaps in the merge target's history, if any. Eventually
5141 we will adjust CHILD->REMAINING_RANGES such that we don't describe
5142 non-existent paths to the editor. */
5143 SVN_ERR(find_gaps_in_merge_source_history(&gap_start, &gap_end,
5144 source,
5145 ra_session, merge_b->ctx,
5146 iterpool));
5147
5148 /* Stash any gap in the merge command baton, we'll need it later when
5149 recording mergeinfo describing this merge. */
5150 if (SVN_IS_VALID_REVNUM(gap_start) && SVN_IS_VALID_REVNUM(gap_end))
5151 merge_b->implicit_src_gap = svn_rangelist__initialize(gap_start, gap_end,
5152 TRUE, result_pool);
5153
5154 for (i = 0; i < children_with_mergeinfo->nelts; i++)
5155 {
5156 svn_client__merge_path_t *child =
5157 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5158 const char *child_repos_path
5159 = svn_dirent_skip_ancestor(merge_b->target->abspath, child->abspath);
5160 merge_source_t child_source;
5161 svn_client__merge_path_t *parent = NULL;
5162 svn_boolean_t child_inherits_implicit;
5163
5164 svn_pool_clear(iterpool);
5165
5166 /* If the path is absent don't do subtree merge either. */
5167 SVN_ERR_ASSERT(child);
5168 if (child->absent)
5169 continue;
5170
5171 SVN_ERR_ASSERT(child_repos_path != NULL);
5172 child_source.loc1 = svn_client__pathrev_join_relpath(
5173 source->loc1, child_repos_path, iterpool);
5174 child_source.loc2 = svn_client__pathrev_join_relpath(
5175 source->loc2, child_repos_path, iterpool);
5176 /* ### Is the child 'ancestral' over the same revision range? It's
5177 * not necessarily true that a child is 'ancestral' if the parent is,
5178 * nor that it's not if the parent is not. However, here we claim
5179 * that it is. Before we had this 'ancestral' field that we need to
5180 * set explicitly, the claim was implicit. Either way, the impact is
5181 * that we might pass calculate_remaining_ranges() a source that is
5182 * not in fact 'ancestral' (despite its 'ancestral' field being true),
5183 * contrary to its doc-string. */
5184 child_source.ancestral = source->ancestral;
5185
5186 /* Get the explicit/inherited mergeinfo for CHILD. If CHILD is the
5187 merge target then also get its implicit mergeinfo. Otherwise defer
5188 this until we know it is absolutely necessary, since it requires an
5189 expensive round trip communication with the server. */
5190 SVN_ERR(get_full_mergeinfo(
5191 child->pre_merge_mergeinfo ? NULL : &(child->pre_merge_mergeinfo),
5192 /* Get implicit only for merge target. */
5193 (i == 0) ? &(child->implicit_mergeinfo) : NULL,
5194 &(child->inherited_mergeinfo),
5195 svn_mergeinfo_inherited, ra_session,
5196 child->abspath,
5197 MAX(source->loc1->rev, source->loc2->rev),
5198 MIN(source->loc1->rev, source->loc2->rev),
5199 merge_b->ctx, result_pool, iterpool));
5200
5201 /* If CHILD isn't the merge target find its parent. */
5202 if (i > 0)
5203 {
5204 parent = find_nearest_ancestor(children_with_mergeinfo,
5205 FALSE, child->abspath);
5206 /* If CHILD is a subtree then its parent must be in
5207 CHILDREN_WITH_MERGEINFO, see the global comment
5208 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
5209 SVN_ERR_ASSERT(parent);
5210 }
5211
5212 /* Issue #3443 - Can CHILD inherit PARENT's implicit mergeinfo, saving
5213 us from having to ask the repos? The only time we can't do this is if
5214 CHILD is the merge target and so there is no PARENT to inherit from
5215 or if CHILD is the root of a switched subtree, in which case PARENT
5216 exists but is not CHILD's repository parent. */
5217 child_inherits_implicit = (parent && !child->switched);
5218
5219 SVN_ERR(calculate_remaining_ranges(parent, child,
5220 &child_source,
5221 child->pre_merge_mergeinfo,
5222 merge_b->implicit_src_gap,
5223 child_inherits_implicit,
5224 ra_session,
5225 merge_b->ctx, result_pool,
5226 iterpool));
5227
5228 /* Deal with any gap in SOURCE's natural history.
5229
5230 If the gap is a proper subset of CHILD->REMAINING_RANGES then we can
5231 safely ignore it since we won't describe this path/rev pair.
5232
5233 If the gap exactly matches or is a superset of a range in
5234 CHILD->REMAINING_RANGES then we must remove that range so we don't
5235 attempt to describe non-existent paths via the reporter, this will
5236 break the editor and our merge.
5237
5238 If the gap adjoins or overlaps a range in CHILD->REMAINING_RANGES
5239 then we must *add* the gap so we span the missing revisions. */
5240 if (child->remaining_ranges->nelts
5241 && merge_b->implicit_src_gap)
5242 {
5243 int j;
5244 svn_boolean_t proper_subset = FALSE;
5245 svn_boolean_t overlaps_or_adjoins = FALSE;
5246
5247 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
5248 so it will work with the svn_rangelist_* APIs below. */
5249 if (source->loc1->rev > source->loc2->rev)
5250 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
5251
5252 for (j = 0; j < child->remaining_ranges->nelts; j++)
5253 {
5254 svn_merge_range_t *range
5255 = APR_ARRAY_IDX(child->remaining_ranges, j, svn_merge_range_t *);
5256
5257 if ((range->start <= gap_start && gap_end < range->end)
5258 || (range->start < gap_start && gap_end <= range->end))
5259 {
5260 proper_subset = TRUE;
5261 break;
5262 }
5263 else if ((gap_start == range->start) && (range->end == gap_end))
5264 {
5265 break;
5266 }
5267 else if (gap_start <= range->end && range->start <= gap_end)
5268 /* intersect */
5269 {
5270 overlaps_or_adjoins = TRUE;
5271 break;
5272 }
5273 }
5274
5275 if (!proper_subset)
5276 {
5277 /* We need to make adjustments. Remove from, or add the gap
5278 to, CHILD->REMAINING_RANGES as appropriate. */
5279
5280 if (overlaps_or_adjoins)
5281 SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
5282 merge_b->implicit_src_gap,
5283 result_pool, iterpool));
5284 else /* equals == TRUE */
5285 SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
5286 merge_b->implicit_src_gap,
5287 child->remaining_ranges, FALSE,
5288 result_pool));
5289 }
5290
5291 if (source->loc1->rev > source->loc2->rev) /* Reverse merge */
5292 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
5293 }
5294 }
5295
5296 svn_pool_destroy(iterpool);
5297 return SVN_NO_ERROR;
5298 }
5299
5300
5301 /*-----------------------------------------------------------------------*/
5302
5303 /*** Other Helper Functions ***/
5304
5305 /* Calculate the new mergeinfo for the target tree rooted at TARGET_ABSPATH
5306 based on MERGES (a mapping of absolute WC paths to rangelists representing
5307 a merge from the source SOURCE_FSPATH).
5308
5309 If RESULT_CATALOG is NULL, then record the new mergeinfo in the WC (at,
5310 and possibly below, TARGET_ABSPATH).
5311
5312 If RESULT_CATALOG is not NULL, then don't record the new mergeinfo on the
5313 WC, but instead record it in RESULT_CATALOG, where the keys are absolute
5314 working copy paths and the values are the new mergeinfos for each.
5315 Allocate additions to RESULT_CATALOG in pool which RESULT_CATALOG was
5316 created in. */
5317 static svn_error_t *
update_wc_mergeinfo(svn_mergeinfo_catalog_t result_catalog,const char * target_abspath,const char * source_fspath,apr_hash_t * merges,svn_boolean_t is_rollback,svn_client_ctx_t * ctx,apr_pool_t * scratch_pool)5318 update_wc_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
5319 const char *target_abspath,
5320 const char *source_fspath,
5321 apr_hash_t *merges,
5322 svn_boolean_t is_rollback,
5323 svn_client_ctx_t *ctx,
5324 apr_pool_t *scratch_pool)
5325 {
5326 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5327 apr_hash_index_t *hi;
5328
5329 /* Combine the mergeinfo for the revision range just merged into
5330 the WC with its on-disk mergeinfo. */
5331 for (hi = apr_hash_first(scratch_pool, merges); hi; hi = apr_hash_next(hi))
5332 {
5333 const char *local_abspath = apr_hash_this_key(hi);
5334 svn_rangelist_t *ranges = apr_hash_this_val(hi);
5335 svn_rangelist_t *rangelist;
5336 svn_error_t *err;
5337 const char *local_abspath_rel_to_target;
5338 const char *fspath;
5339 svn_mergeinfo_t mergeinfo;
5340
5341 svn_pool_clear(iterpool);
5342
5343 /* As some of the merges may've changed the WC's mergeinfo, get
5344 a fresh copy before using it to update the WC's mergeinfo. */
5345 err = svn_client__parse_mergeinfo(&mergeinfo, ctx->wc_ctx,
5346 local_abspath, iterpool, iterpool);
5347
5348 /* If a directory PATH was skipped because it is missing or was
5349 obstructed by an unversioned item then there's nothing we can
5350 do with that, so skip it. */
5351 if (err)
5352 {
5353 if (err->apr_err == SVN_ERR_WC_NOT_LOCKED
5354 || err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5355 {
5356 svn_error_clear(err);
5357 continue;
5358 }
5359 else
5360 {
5361 return svn_error_trace(err);
5362 }
5363 }
5364
5365 /* If we are attempting to set empty revision range override mergeinfo
5366 on a path with no explicit mergeinfo, we first need the
5367 mergeinfo that path inherits. */
5368 if (mergeinfo == NULL && ranges->nelts == 0)
5369 {
5370 SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, NULL,
5371 svn_mergeinfo_nearest_ancestor,
5372 local_abspath, NULL, NULL,
5373 FALSE, ctx, iterpool, iterpool));
5374 }
5375
5376 if (mergeinfo == NULL)
5377 mergeinfo = apr_hash_make(iterpool);
5378
5379 local_abspath_rel_to_target = svn_dirent_skip_ancestor(target_abspath,
5380 local_abspath);
5381 SVN_ERR_ASSERT(local_abspath_rel_to_target != NULL);
5382 fspath = svn_fspath__join(source_fspath,
5383 local_abspath_rel_to_target,
5384 iterpool);
5385 rangelist = svn_hash_gets(mergeinfo, fspath);
5386 if (rangelist == NULL)
5387 rangelist = apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
5388
5389 if (is_rollback)
5390 {
5391 ranges = svn_rangelist_dup(ranges, iterpool);
5392 SVN_ERR(svn_rangelist_reverse(ranges, iterpool));
5393 SVN_ERR(svn_rangelist_remove(&rangelist, ranges, rangelist,
5394 FALSE,
5395 iterpool));
5396 }
5397 else
5398 {
5399 SVN_ERR(svn_rangelist_merge2(rangelist, ranges, iterpool, iterpool));
5400 }
5401 /* Update the mergeinfo by adjusting the path's rangelist. */
5402 svn_hash_sets(mergeinfo, fspath, rangelist);
5403
5404 if (is_rollback && apr_hash_count(mergeinfo) == 0)
5405 mergeinfo = NULL;
5406
5407 svn_mergeinfo__remove_empty_rangelists(mergeinfo, scratch_pool);
5408
5409 if (result_catalog)
5410 {
5411 svn_mergeinfo_t existing_mergeinfo =
5412 svn_hash_gets(result_catalog, local_abspath);
5413 apr_pool_t *result_catalog_pool = apr_hash_pool_get(result_catalog);
5414
5415 if (existing_mergeinfo)
5416 SVN_ERR(svn_mergeinfo_merge2(mergeinfo, existing_mergeinfo,
5417 result_catalog_pool, scratch_pool));
5418 svn_hash_sets(result_catalog,
5419 apr_pstrdup(result_catalog_pool, local_abspath),
5420 svn_mergeinfo_dup(mergeinfo, result_catalog_pool));
5421 }
5422 else
5423 {
5424 err = svn_client__record_wc_mergeinfo(local_abspath, mergeinfo,
5425 TRUE, ctx, iterpool);
5426
5427 if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
5428 {
5429 /* PATH isn't just missing, it's not even versioned as far
5430 as this working copy knows. But it was included in
5431 MERGES, which means that the server knows about it.
5432 Likely we don't have access to the source due to authz
5433 restrictions. For now just clear the error and
5434 continue...
5435
5436 ### TODO: Set non-inheritable mergeinfo on PATH's immediate
5437 ### parent and normal mergeinfo on PATH's siblings which we
5438 ### do have access to. */
5439 svn_error_clear(err);
5440 }
5441 else
5442 SVN_ERR(err);
5443 }
5444 }
5445
5446 svn_pool_destroy(iterpool);
5447 return SVN_NO_ERROR;
5448 }
5449
5450 /* Helper for record_mergeinfo_for_dir_merge().
5451
5452 Record override mergeinfo on any paths skipped during a merge.
5453
5454 Set empty mergeinfo on each path in MERGE_B->SKIPPED_ABSPATHS so the path
5455 does not incorrectly inherit mergeinfo that will later be describing
5456 the merge.
5457
5458 MERGEINFO_PATH and MERGE_B are cascaded from
5459 arguments of the same name in the caller.
5460
5461 IS_ROLLBACK is true if the caller is recording a reverse merge and false
5462 otherwise. RANGELIST is the set of revisions being merged from
5463 MERGEINFO_PATH to MERGE_B->target. */
5464 static svn_error_t *
record_skips_in_mergeinfo(const char * mergeinfo_path,const svn_rangelist_t * rangelist,svn_boolean_t is_rollback,merge_cmd_baton_t * merge_b,apr_pool_t * scratch_pool)5465 record_skips_in_mergeinfo(const char *mergeinfo_path,
5466 const svn_rangelist_t *rangelist,
5467 svn_boolean_t is_rollback,
5468 merge_cmd_baton_t *merge_b,
5469 apr_pool_t *scratch_pool)
5470 {
5471 apr_hash_index_t *hi;
5472 apr_hash_t *merges;
5473 apr_size_t nbr_skips = apr_hash_count(merge_b->skipped_abspaths);
5474 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5475
5476 if (nbr_skips == 0)
5477 return SVN_NO_ERROR;
5478
5479 merges = apr_hash_make(scratch_pool);
5480
5481 /* Override the mergeinfo for child paths which weren't actually merged. */
5482 for (hi = apr_hash_first(scratch_pool, merge_b->skipped_abspaths); hi;
5483 hi = apr_hash_next(hi))
5484 {
5485 const char *skipped_abspath = apr_hash_this_key(hi);
5486 svn_wc_notify_state_t obstruction_state;
5487
5488 svn_pool_clear(iterpool);
5489
5490 /* Before we override, make sure this is a versioned path, it might
5491 be an external or missing from disk due to authz restrictions. */
5492 SVN_ERR(perform_obstruction_check(&obstruction_state, NULL, NULL,
5493 NULL, NULL,
5494 merge_b, skipped_abspath,
5495 iterpool));
5496 if (obstruction_state == svn_wc_notify_state_obstructed
5497 || obstruction_state == svn_wc_notify_state_missing)
5498 continue;
5499
5500 /* Add an empty range list for this path.
5501
5502 ### TODO: This works fine for a file path skipped because it is
5503 ### missing as long as the file's parent directory is present.
5504 ### But missing directory paths skipped are not handled yet,
5505 ### see issue #2915.
5506
5507 ### TODO: An empty range is fine if the skipped path doesn't
5508 ### inherit any mergeinfo from a parent, but if it does
5509 ### we need to account for that. See issue #3440
5510 ### https://issues.apache.org/jira/browse/SVN-3440. */
5511 svn_hash_sets(merges, skipped_abspath,
5512 apr_array_make(scratch_pool, 0,
5513 sizeof(svn_merge_range_t *)));
5514
5515 /* if (nbr_skips < notify_b->nbr_notifications)
5516 ### Use RANGELIST as the mergeinfo for all children of
5517 ### this path which were not also explicitly
5518 ### skipped? */
5519 }
5520 SVN_ERR(update_wc_mergeinfo(NULL, merge_b->target->abspath,
5521 mergeinfo_path, merges,
5522 is_rollback, merge_b->ctx, iterpool));
5523 svn_pool_destroy(iterpool);
5524 return SVN_NO_ERROR;
5525 }
5526
5527 /* Data for reporting when a merge aborted because of raising conflicts.
5528 */
5529 typedef struct single_range_conflict_report_t
5530 {
5531 /* What sub-range of the requested source raised conflicts?
5532 * The 'inheritable' flag is ignored. */
5533 merge_source_t *conflicted_range;
5534 /* What sub-range of the requested source remains to be merged?
5535 * NULL if no more. The 'inheritable' flag is ignored. */
5536 merge_source_t *remaining_source;
5537
5538 } single_range_conflict_report_t;
5539
5540 /* Create a single_range_conflict_report_t, containing deep copies of
5541 * CONFLICTED_RANGE and REMAINING_SOURCE, allocated in RESULT_POOL. */
5542 static single_range_conflict_report_t *
single_range_conflict_report_create(const merge_source_t * conflicted_range,const merge_source_t * remaining_source,apr_pool_t * result_pool)5543 single_range_conflict_report_create(const merge_source_t *conflicted_range,
5544 const merge_source_t *remaining_source,
5545 apr_pool_t *result_pool)
5546 {
5547 single_range_conflict_report_t *report
5548 = apr_palloc(result_pool, sizeof(*report));
5549
5550 assert(conflicted_range != NULL);
5551
5552 report->conflicted_range = merge_source_dup(conflicted_range, result_pool);
5553 report->remaining_source
5554 = remaining_source ? merge_source_dup(remaining_source, result_pool)
5555 : NULL;
5556 return report;
5557 }
5558
5559 /* Return a new svn_client__conflict_report_t containing deep copies of the
5560 * parameters, allocated in RESULT_POOL. */
5561 static svn_client__conflict_report_t *
conflict_report_create(const char * target_abspath,const merge_source_t * conflicted_range,svn_boolean_t was_last_range,apr_pool_t * result_pool)5562 conflict_report_create(const char *target_abspath,
5563 const merge_source_t *conflicted_range,
5564 svn_boolean_t was_last_range,
5565 apr_pool_t *result_pool)
5566 {
5567 svn_client__conflict_report_t *report = apr_palloc(result_pool,
5568 sizeof(*report));
5569
5570 report->target_abspath = apr_pstrdup(result_pool, target_abspath);
5571 report->conflicted_range = merge_source_dup(conflicted_range, result_pool);
5572 report->was_last_range = was_last_range;
5573 return report;
5574 }
5575
5576 /* Return a deep copy of REPORT, allocated in RESULT_POOL. */
5577 static svn_client__conflict_report_t *
conflict_report_dup(const svn_client__conflict_report_t * report,apr_pool_t * result_pool)5578 conflict_report_dup(const svn_client__conflict_report_t *report,
5579 apr_pool_t *result_pool)
5580 {
5581 svn_client__conflict_report_t *new = apr_pmemdup(result_pool, report,
5582 sizeof(*new));
5583
5584 new->target_abspath = apr_pstrdup(result_pool, report->target_abspath);
5585 new->conflicted_range = merge_source_dup(report->conflicted_range,
5586 result_pool);
5587 return new;
5588 }
5589
5590 svn_error_t *
svn_client__make_merge_conflict_error(svn_client__conflict_report_t * report,apr_pool_t * scratch_pool)5591 svn_client__make_merge_conflict_error(svn_client__conflict_report_t *report,
5592 apr_pool_t *scratch_pool)
5593 {
5594 assert(!report || svn_dirent_is_absolute(report->target_abspath));
5595
5596 if (report && ! report->was_last_range)
5597 {
5598 svn_error_t *err = svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
5599 _("One or more conflicts were produced while merging r%ld:%ld into\n"
5600 "'%s' --\n"
5601 "resolve all conflicts and rerun the merge to apply the remaining\n"
5602 "unmerged revisions"),
5603 report->conflicted_range->loc1->rev, report->conflicted_range->loc2->rev,
5604 svn_dirent_local_style(report->target_abspath, scratch_pool));
5605 assert(report->conflicted_range->loc1->rev != report->conflicted_range->loc2->rev); /* ### is a valid case in a 2-URL merge */
5606 return err;
5607 }
5608 return SVN_NO_ERROR;
5609 }
5610
5611 /* Helper for do_directory_merge().
5612
5613 TARGET_WCPATH is a directory and CHILDREN_WITH_MERGEINFO is filled
5614 with paths (svn_client__merge_path_t *) arranged in depth first order,
5615 which have mergeinfo set on them or meet one of the other criteria
5616 defined in get_mergeinfo_paths(). Remove any paths absent from disk
5617 from CHILDREN_WITH_MERGEINFO which are equal to
5618 or are descendants of TARGET_WCPATH by setting those children to NULL. */
5619 static svn_error_t *
remove_absent_children(const char * target_wcpath,apr_array_header_t * children_with_mergeinfo)5620 remove_absent_children(const char *target_wcpath,
5621 apr_array_header_t *children_with_mergeinfo)
5622 {
5623 /* Before we try to override mergeinfo for skipped paths, make sure
5624 the path isn't absent due to authz restrictions, because there's
5625 nothing we can do about those. */
5626 int i;
5627 for (i = 0; i < children_with_mergeinfo->nelts; i++)
5628 {
5629 svn_client__merge_path_t *child =
5630 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5631 if (child->absent
5632 && svn_dirent_is_ancestor(target_wcpath, child->abspath))
5633 {
5634 SVN_ERR(svn_sort__array_delete2(children_with_mergeinfo, i--, 1));
5635 }
5636 }
5637 return SVN_NO_ERROR;
5638 }
5639
5640 /* Helper for do_directory_merge() to handle the case where a merge editor
5641 drive removes explicit mergeinfo from a subtree of the merge target.
5642
5643 MERGE_B is cascaded from the argument of the same name in
5644 do_directory_merge(). For each path (if any) in
5645 MERGE_B->PATHS_WITH_DELETED_MERGEINFO remove that path from
5646 CHILDREN_WITH_MERGEINFO.
5647
5648 The one exception is for the merge target itself,
5649 MERGE_B->target->abspath, this must always be present in
5650 CHILDREN_WITH_MERGEINFO so this is never removed by this
5651 function. */
5652 static svn_error_t *
remove_children_with_deleted_mergeinfo(merge_cmd_baton_t * merge_b,apr_array_header_t * children_with_mergeinfo)5653 remove_children_with_deleted_mergeinfo(merge_cmd_baton_t *merge_b,
5654 apr_array_header_t *children_with_mergeinfo)
5655 {
5656 int i;
5657
5658 if (!merge_b->paths_with_deleted_mergeinfo)
5659 return SVN_NO_ERROR;
5660
5661 /* CHILDREN_WITH_MERGEINFO[0] is the always the merge target
5662 so start at the first child. */
5663 for (i = 1; i < children_with_mergeinfo->nelts; i++)
5664 {
5665 svn_client__merge_path_t *child =
5666 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5667
5668 if (svn_hash_gets(merge_b->paths_with_deleted_mergeinfo, child->abspath))
5669 {
5670 SVN_ERR(svn_sort__array_delete2(children_with_mergeinfo, i--, 1));
5671 }
5672 }
5673 return SVN_NO_ERROR;
5674 }
5675
5676 /* Helper for do_directory_merge().
5677
5678 Set up the diff editor report to merge the SOURCE diff
5679 into TARGET_ABSPATH and drive it.
5680
5681 If mergeinfo is not being honored (based on MERGE_B -- see the doc
5682 string for HONOR_MERGEINFO() for how this is determined), then ignore
5683 CHILDREN_WITH_MERGEINFO and merge the SOURCE diff to TARGET_ABSPATH.
5684
5685 If mergeinfo is being honored then perform a history-aware merge,
5686 describing TARGET_ABSPATH and its subtrees to the reporter in such as way
5687 as to avoid repeating merges already performed per the mergeinfo and
5688 natural history of TARGET_ABSPATH and its subtrees.
5689
5690 The ranges that still need to be merged to the TARGET_ABSPATH and its
5691 subtrees are described in CHILDREN_WITH_MERGEINFO, an array of
5692 svn_client__merge_path_t * -- see 'THE CHILDREN_WITH_MERGEINFO ARRAY'
5693 comment at the top of this file for more info. Note that it is possible
5694 TARGET_ABSPATH and/or some of its subtrees need only a subset, or no part,
5695 of SOURCE to be merged. Though there is little point to
5696 calling this function if TARGET_ABSPATH and all its subtrees have already
5697 had SOURCE merged, this will work but is a no-op.
5698
5699 SOURCE->rev1 and SOURCE->rev2 must be bound by the set of remaining_ranges
5700 fields in CHILDREN_WITH_MERGEINFO's elements, specifically:
5701
5702 For forward merges (SOURCE->rev1 < SOURCE->rev2):
5703
5704 1) The first svn_merge_range_t * element of each child's remaining_ranges
5705 array must meet one of the following conditions:
5706
5707 a) The range's start field is greater than or equal to SOURCE->rev2.
5708
5709 b) The range's end field is SOURCE->rev2.
5710
5711 2) Among all the ranges that meet condition 'b' the oldest start
5712 revision must equal SOURCE->rev1.
5713
5714 For reverse merges (SOURCE->rev1 > SOURCE->rev2):
5715
5716 1) The first svn_merge_range_t * element of each child's remaining_ranges
5717 array must meet one of the following conditions:
5718
5719 a) The range's start field is less than or equal to SOURCE->rev2.
5720
5721 b) The range's end field is SOURCE->rev2.
5722
5723 2) Among all the ranges that meet condition 'b' the youngest start
5724 revision must equal SOURCE->rev1.
5725
5726 Note: If the first svn_merge_range_t * element of some subtree child's
5727 remaining_ranges array is the same as the first range of that child's
5728 nearest path-wise ancestor, then the subtree child *will not* be described
5729 to the reporter.
5730
5731 DEPTH, NOTIFY_B, and MERGE_B are cascaded from do_directory_merge(), see
5732 that function for more info.
5733
5734 MERGE_B->ra_session1 and MERGE_B->ra_session2 are RA sessions open to any
5735 URL in the repository of SOURCE; they may be temporarily reparented within
5736 this function.
5737
5738 If SOURCE->ancestral is set, then SOURCE->loc1 must be a
5739 historical ancestor of SOURCE->loc2, or vice-versa (see
5740 `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements around
5741 SOURCE in this case).
5742 */
5743 static svn_error_t *
drive_merge_report_editor(const char * target_abspath,const merge_source_t * source,const apr_array_header_t * children_with_mergeinfo,const svn_diff_tree_processor_t * processor,svn_depth_t depth,merge_cmd_baton_t * merge_b,apr_pool_t * scratch_pool)5744 drive_merge_report_editor(const char *target_abspath,
5745 const merge_source_t *source,
5746 const apr_array_header_t *children_with_mergeinfo,
5747 const svn_diff_tree_processor_t *processor,
5748 svn_depth_t depth,
5749 merge_cmd_baton_t *merge_b,
5750 apr_pool_t *scratch_pool)
5751 {
5752 const svn_ra_reporter3_t *reporter;
5753 const svn_delta_editor_t *diff_editor;
5754 void *diff_edit_baton;
5755 void *report_baton;
5756 svn_revnum_t target_start;
5757 svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
5758 const char *old_sess1_url, *old_sess2_url;
5759 svn_boolean_t is_rollback = source->loc1->rev > source->loc2->rev;
5760
5761 /* Start with a safe default starting revision for the editor and the
5762 merge target. */
5763 target_start = source->loc1->rev;
5764
5765 /* If we are honoring mergeinfo the starting revision for the merge target
5766 might not be SOURCE->rev1, in fact the merge target might not need *any*
5767 part of SOURCE merged -- Instead some subtree of the target
5768 needs SOURCE -- So get the right starting revision for the
5769 target. */
5770 if (honor_mergeinfo)
5771 {
5772 svn_client__merge_path_t *child;
5773
5774 /* CHILDREN_WITH_MERGEINFO must always exist if we are honoring
5775 mergeinfo and must have at least one element (describing the
5776 merge target). */
5777 SVN_ERR_ASSERT(children_with_mergeinfo);
5778 SVN_ERR_ASSERT(children_with_mergeinfo->nelts);
5779
5780 /* Get the merge target's svn_client__merge_path_t, which is always
5781 the first in the array due to depth first sorting requirement,
5782 see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
5783 child = APR_ARRAY_IDX(children_with_mergeinfo, 0,
5784 svn_client__merge_path_t *);
5785 SVN_ERR_ASSERT(child);
5786 if (child->remaining_ranges->nelts == 0)
5787 {
5788 /* The merge target doesn't need anything merged. */
5789 target_start = source->loc2->rev;
5790 }
5791 else
5792 {
5793 /* The merge target has remaining revisions to merge. These
5794 ranges may fully or partially overlap the range described
5795 by SOURCE->rev1:rev2 or may not intersect that range at
5796 all. */
5797 svn_merge_range_t *range =
5798 APR_ARRAY_IDX(child->remaining_ranges, 0,
5799 svn_merge_range_t *);
5800 if ((!is_rollback && range->start > source->loc2->rev)
5801 || (is_rollback && range->start < source->loc2->rev))
5802 {
5803 /* Merge target's first remaining range doesn't intersect. */
5804 target_start = source->loc2->rev;
5805 }
5806 else
5807 {
5808 /* Merge target's first remaining range partially or
5809 fully overlaps. */
5810 target_start = range->start;
5811 }
5812 }
5813 }
5814
5815 SVN_ERR(svn_client__ensure_ra_session_url(&old_sess1_url,
5816 merge_b->ra_session1,
5817 source->loc1->url, scratch_pool));
5818 /* Temporarily point our second RA session to SOURCE->loc1->url, too. We use
5819 this to request individual file contents. */
5820 SVN_ERR(svn_client__ensure_ra_session_url(&old_sess2_url,
5821 merge_b->ra_session2,
5822 source->loc1->url, scratch_pool));
5823
5824 /* Get the diff editor and a reporter with which to, ultimately,
5825 drive it. */
5826 SVN_ERR(svn_client__get_diff_editor2(&diff_editor, &diff_edit_baton,
5827 merge_b->ra_session2,
5828 depth,
5829 source->loc1->rev,
5830 TRUE /* text_deltas */,
5831 processor,
5832 merge_b->ctx->cancel_func,
5833 merge_b->ctx->cancel_baton,
5834 scratch_pool));
5835 SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1,
5836 &reporter, &report_baton, source->loc2->rev,
5837 "", depth, merge_b->diff_ignore_ancestry,
5838 TRUE, /* text_deltas */
5839 source->loc2->url, diff_editor, diff_edit_baton,
5840 scratch_pool));
5841
5842 /* Drive the reporter. */
5843 SVN_ERR(reporter->set_path(report_baton, "", target_start, depth,
5844 FALSE, NULL, scratch_pool));
5845 if (honor_mergeinfo && children_with_mergeinfo)
5846 {
5847 /* Describe children with mergeinfo overlapping this merge
5848 operation such that no repeated diff is retrieved for them from
5849 the repository. */
5850 int i;
5851 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5852
5853 /* Start with CHILDREN_WITH_MERGEINFO[1], CHILDREN_WITH_MERGEINFO[0]
5854 is always the merge target (TARGET_ABSPATH). */
5855 for (i = 1; i < children_with_mergeinfo->nelts; i++)
5856 {
5857 svn_merge_range_t *range;
5858 const char *child_repos_path;
5859 const svn_client__merge_path_t *parent;
5860 const svn_client__merge_path_t *child =
5861 APR_ARRAY_IDX(children_with_mergeinfo, i,
5862 svn_client__merge_path_t *);
5863
5864 SVN_ERR_ASSERT(child);
5865 if (child->absent)
5866 continue;
5867
5868 svn_pool_clear(iterpool);
5869
5870 /* Find this child's nearest wc ancestor with mergeinfo. */
5871 parent = find_nearest_ancestor(children_with_mergeinfo,
5872 FALSE, child->abspath);
5873
5874 /* If a subtree needs the same range applied as its nearest parent
5875 with mergeinfo or neither the subtree nor this parent need
5876 SOURCE->rev1:rev2 merged, then we don't need to describe the
5877 subtree separately. In the latter case this could break the
5878 editor if child->abspath didn't exist at SOURCE->rev2 and we
5879 attempt to describe it via a reporter set_path call. */
5880 if (child->remaining_ranges->nelts)
5881 {
5882 range = APR_ARRAY_IDX(child->remaining_ranges, 0,
5883 svn_merge_range_t *);
5884 if ((!is_rollback && range->start > source->loc2->rev)
5885 || (is_rollback && range->start < source->loc2->rev))
5886 {
5887 /* This child's first remaining range comes after the range
5888 we are currently merging, so skip it. We expect to get
5889 to it in a subsequent call to this function. */
5890 continue;
5891 }
5892 else if (parent->remaining_ranges->nelts)
5893 {
5894 svn_merge_range_t *parent_range =
5895 APR_ARRAY_IDX(parent->remaining_ranges, 0,
5896 svn_merge_range_t *);
5897 svn_merge_range_t *child_range =
5898 APR_ARRAY_IDX(child->remaining_ranges, 0,
5899 svn_merge_range_t *);
5900 if (parent_range->start == child_range->start)
5901 continue; /* Subtree needs same range as parent. */
5902 }
5903 }
5904 else /* child->remaining_ranges->nelts == 0*/
5905 {
5906 /* If both the subtree and its parent need no ranges applied
5907 consider that as the "same ranges" and don't describe
5908 the subtree. */
5909 if (parent->remaining_ranges->nelts == 0)
5910 continue;
5911 }
5912
5913 /* Ok, we really need to describe this subtree as it needs different
5914 ranges applied than its nearest working copy parent. */
5915 child_repos_path = svn_dirent_is_child(target_abspath,
5916 child->abspath,
5917 iterpool);
5918 /* This loop is only processing subtrees, so CHILD->ABSPATH
5919 better be a proper child of the merge target. */
5920 SVN_ERR_ASSERT(child_repos_path);
5921
5922 if ((child->remaining_ranges->nelts == 0)
5923 || (is_rollback && (range->start < source->loc2->rev))
5924 || (!is_rollback && (range->start > source->loc2->rev)))
5925 {
5926 /* Nothing to merge to this child. We'll claim we have
5927 it up to date so the server doesn't send us
5928 anything. */
5929 SVN_ERR(reporter->set_path(report_baton, child_repos_path,
5930 source->loc2->rev, depth, FALSE,
5931 NULL, iterpool));
5932 }
5933 else
5934 {
5935 SVN_ERR(reporter->set_path(report_baton, child_repos_path,
5936 range->start, depth, FALSE,
5937 NULL, iterpool));
5938 }
5939 }
5940 svn_pool_destroy(iterpool);
5941 }
5942 SVN_ERR(reporter->finish_report(report_baton, scratch_pool));
5943
5944 /* Point the merge baton's RA sessions back where they were. */
5945 SVN_ERR(svn_ra_reparent(merge_b->ra_session1, old_sess1_url, scratch_pool));
5946 SVN_ERR(svn_ra_reparent(merge_b->ra_session2, old_sess2_url, scratch_pool));
5947
5948 return SVN_NO_ERROR;
5949 }
5950
5951 /* Iterate over each svn_client__merge_path_t * element in
5952 CHILDREN_WITH_MERGEINFO and, if START_REV is true, find the most inclusive
5953 start revision among those element's first remaining_ranges element. If
5954 START_REV is false, then look for the most inclusive end revision.
5955
5956 If IS_ROLLBACK is true the youngest start or end (as per START_REV)
5957 revision is considered the "most inclusive" otherwise the oldest revision
5958 is.
5959
5960 If none of CHILDREN_WITH_MERGEINFO's elements have any remaining ranges
5961 return SVN_INVALID_REVNUM. */
5962 static svn_revnum_t
get_most_inclusive_rev(const apr_array_header_t * children_with_mergeinfo,svn_boolean_t is_rollback,svn_boolean_t start_rev)5963 get_most_inclusive_rev(const apr_array_header_t *children_with_mergeinfo,
5964 svn_boolean_t is_rollback,
5965 svn_boolean_t start_rev)
5966 {
5967 int i;
5968 svn_revnum_t most_inclusive_rev = SVN_INVALID_REVNUM;
5969
5970 for (i = 0; i < children_with_mergeinfo->nelts; i++)
5971 {
5972 svn_client__merge_path_t *child =
5973 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5974
5975 if ((! child) || child->absent)
5976 continue;
5977 if (child->remaining_ranges->nelts > 0)
5978 {
5979 svn_merge_range_t *range =
5980 APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
5981
5982 /* Are we looking for the most inclusive start or end rev? */
5983 svn_revnum_t rev = start_rev ? range->start : range->end;
5984
5985 if ((most_inclusive_rev == SVN_INVALID_REVNUM)
5986 || (is_rollback && (rev > most_inclusive_rev))
5987 || ((! is_rollback) && (rev < most_inclusive_rev)))
5988 most_inclusive_rev = rev;
5989 }
5990 }
5991 return most_inclusive_rev;
5992 }
5993
5994
5995 /* If first item in each child of CHILDREN_WITH_MERGEINFO's
5996 remaining_ranges is inclusive of END_REV, Slice the first range in
5997 to two at END_REV. All the allocations are persistent and allocated
5998 from POOL. */
5999 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)6000 slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
6001 svn_boolean_t is_rollback, svn_revnum_t end_rev,
6002 apr_pool_t *pool)
6003 {
6004 int i;
6005 for (i = 0; i < children_with_mergeinfo->nelts; i++)
6006 {
6007 svn_client__merge_path_t *child =
6008 APR_ARRAY_IDX(children_with_mergeinfo, i,
6009 svn_client__merge_path_t *);
6010 if (!child || child->absent)
6011 continue;
6012 if (child->remaining_ranges->nelts > 0)
6013 {
6014 svn_merge_range_t *range = APR_ARRAY_IDX(child->remaining_ranges, 0,
6015 svn_merge_range_t *);
6016 if ((is_rollback && (range->start > end_rev)
6017 && (range->end < end_rev))
6018 || (!is_rollback && (range->start < end_rev)
6019 && (range->end > end_rev)))
6020 {
6021 svn_merge_range_t *split_range1, *split_range2;
6022
6023 split_range1 = svn_merge_range_dup(range, pool);
6024 split_range2 = svn_merge_range_dup(range, pool);
6025 split_range1->end = end_rev;
6026 split_range2->start = end_rev;
6027 APR_ARRAY_IDX(child->remaining_ranges, 0,
6028 svn_merge_range_t *) = split_range1;
6029 SVN_ERR(svn_sort__array_insert2(child->remaining_ranges,
6030 &split_range2, 1));
6031 }
6032 }
6033 }
6034 return SVN_NO_ERROR;
6035 }
6036
6037 /* Helper for do_directory_merge().
6038
6039 For each child in CHILDREN_WITH_MERGEINFO remove the first remaining_ranges
6040 svn_merge_range_t *element of the child if that range has an end revision
6041 equal to REVISION.
6042
6043 If a range is removed from a child's remaining_ranges array, allocate the
6044 new remaining_ranges array in POOL.
6045 */
6046 static svn_error_t *
remove_first_range_from_remaining_ranges(svn_revnum_t revision,apr_array_header_t * children_with_mergeinfo,apr_pool_t * pool)6047 remove_first_range_from_remaining_ranges(svn_revnum_t revision,
6048 apr_array_header_t
6049 *children_with_mergeinfo,
6050 apr_pool_t *pool)
6051 {
6052 int i;
6053
6054 for (i = 0; i < children_with_mergeinfo->nelts; i++)
6055 {
6056 svn_client__merge_path_t *child =
6057 APR_ARRAY_IDX(children_with_mergeinfo, i,
6058 svn_client__merge_path_t *);
6059 if (!child || child->absent)
6060 continue;
6061 if (child->remaining_ranges->nelts > 0)
6062 {
6063 svn_merge_range_t *first_range =
6064 APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
6065 if (first_range->end == revision)
6066 {
6067 SVN_ERR(svn_sort__array_delete2(child->remaining_ranges, 0, 1));
6068 }
6069 }
6070 }
6071 return SVN_NO_ERROR;
6072 }
6073
6074 /* Get a file's content and properties from the repository.
6075 Set *FILENAME to the local path to a new temporary file holding its text,
6076 and set *PROPS to a new hash of its properties.
6077
6078 RA_SESSION is a session open to the correct repository, which will be
6079 temporarily reparented to the URL of the file itself. LOCATION is the
6080 repository location of the file.
6081
6082 The resulting file and the return values live as long as RESULT_POOL, all
6083 other allocations occur in SCRATCH_POOL.
6084 */
6085 static svn_error_t *
single_file_merge_get_file(const char ** filename,apr_hash_t ** props,svn_ra_session_t * ra_session,const svn_client__pathrev_t * location,const char * wc_target,apr_pool_t * result_pool,apr_pool_t * scratch_pool)6086 single_file_merge_get_file(const char **filename,
6087 apr_hash_t **props,
6088 svn_ra_session_t *ra_session,
6089 const svn_client__pathrev_t *location,
6090 const char *wc_target,
6091 apr_pool_t *result_pool,
6092 apr_pool_t *scratch_pool)
6093 {
6094 svn_stream_t *stream;
6095 const char *old_sess_url;
6096 svn_error_t *err;
6097
6098 SVN_ERR(svn_stream_open_unique(&stream, filename, NULL,
6099 svn_io_file_del_on_pool_cleanup,
6100 result_pool, scratch_pool));
6101
6102 SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url, ra_session, location->url,
6103 scratch_pool));
6104 err = svn_ra_get_file(ra_session, "", location->rev,
6105 stream, NULL, props, scratch_pool);
6106 SVN_ERR(svn_error_compose_create(
6107 err, svn_ra_reparent(ra_session, old_sess_url, scratch_pool)));
6108
6109 return svn_error_trace(svn_stream_close(stream));
6110 }
6111
6112 /* Compare two svn_client__merge_path_t elements **A and **B, given the
6113 addresses of pointers to them. Return an integer less than, equal to, or
6114 greater than zero if A sorts before, the same as, or after B, respectively.
6115 This is a helper for qsort() and bsearch() on an array of such elements. */
6116 static int
compare_merge_path_t_as_paths(const void * a,const void * b)6117 compare_merge_path_t_as_paths(const void *a,
6118 const void *b)
6119 {
6120 const svn_client__merge_path_t *child1
6121 = *((const svn_client__merge_path_t * const *) a);
6122 const svn_client__merge_path_t *child2
6123 = *((const svn_client__merge_path_t * const *) b);
6124
6125 return svn_path_compare_paths(child1->abspath, child2->abspath);
6126 }
6127
6128 /* Return a pointer to the element of CHILDREN_WITH_MERGEINFO whose path
6129 * is PATH, or return NULL if there is no such element. */
6130 static svn_client__merge_path_t *
get_child_with_mergeinfo(const apr_array_header_t * children_with_mergeinfo,const char * abspath)6131 get_child_with_mergeinfo(const apr_array_header_t *children_with_mergeinfo,
6132 const char *abspath)
6133 {
6134 svn_client__merge_path_t merge_path;
6135 svn_client__merge_path_t *key;
6136 svn_client__merge_path_t **pchild;
6137
6138 merge_path.abspath = abspath;
6139 key = &merge_path;
6140 pchild = bsearch(&key, children_with_mergeinfo->elts,
6141 children_with_mergeinfo->nelts,
6142 children_with_mergeinfo->elt_size,
6143 compare_merge_path_t_as_paths);
6144 return pchild ? *pchild : NULL;
6145 }
6146
6147 /* Insert a deep copy of INSERT_ELEMENT into the CHILDREN_WITH_MERGEINFO
6148 array at its correct position. Allocate the new storage in POOL.
6149 CHILDREN_WITH_MERGEINFO is a depth first sorted array of
6150 (svn_client__merge_path_t *).
6151
6152 ### Most callers don't need this to deep-copy the new element.
6153 ### It may be more efficient for some callers to insert a bunch of items
6154 out of order and then sort afterwards. (One caller is doing a qsort
6155 after calling this anyway.)
6156 */
6157 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)6158 insert_child_to_merge(apr_array_header_t *children_with_mergeinfo,
6159 const svn_client__merge_path_t *insert_element,
6160 apr_pool_t *pool)
6161 {
6162 int insert_index;
6163 const svn_client__merge_path_t *new_element;
6164
6165 /* Find where to insert the new element */
6166 insert_index =
6167 svn_sort__bsearch_lower_bound(children_with_mergeinfo, &insert_element,
6168 compare_merge_path_t_as_paths);
6169
6170 new_element = svn_client__merge_path_dup(insert_element, pool);
6171 SVN_ERR(svn_sort__array_insert2(children_with_mergeinfo,
6172 &new_element, insert_index));
6173 return SVN_NO_ERROR;
6174 }
6175
6176 /* Helper for get_mergeinfo_paths().
6177
6178 CHILDREN_WITH_MERGEINFO, DEPTH, and POOL are
6179 all cascaded from the arguments of the same name to get_mergeinfo_paths().
6180
6181 TARGET is the merge target.
6182
6183 *CHILD is the element in in CHILDREN_WITH_MERGEINFO that
6184 get_mergeinfo_paths() is iterating over and *CURR_INDEX is index for
6185 *CHILD.
6186
6187 If CHILD->ABSPATH is equal to MERGE_CMD_BATON->target->abspath do nothing.
6188 Else if CHILD->ABSPATH is switched or absent then make sure its immediate
6189 (as opposed to nearest) parent in CHILDREN_WITH_MERGEINFO is marked as
6190 missing a child. If the immediate parent does not exist in
6191 CHILDREN_WITH_MERGEINFO then create it (and increment *CURR_INDEX so that
6192 caller doesn't process the inserted element). Also ensure that
6193 CHILD->ABSPATH's siblings which are not already present in
6194 CHILDREN_WITH_MERGEINFO are also added to the array, limited by DEPTH
6195 (e.g. don't add directory siblings of a switched file).
6196 Use POOL for temporary allocations only, any new CHILDREN_WITH_MERGEINFO
6197 elements are allocated in POOL. */
6198 static svn_error_t *
insert_parent_and_sibs_of_sw_absent_del_subtree(apr_array_header_t * children_with_mergeinfo,const merge_target_t * target,int * curr_index,svn_client__merge_path_t * child,svn_depth_t depth,svn_client_ctx_t * ctx,apr_pool_t * pool)6199 insert_parent_and_sibs_of_sw_absent_del_subtree(
6200 apr_array_header_t *children_with_mergeinfo,
6201 const merge_target_t *target,
6202 int *curr_index,
6203 svn_client__merge_path_t *child,
6204 svn_depth_t depth,
6205 svn_client_ctx_t *ctx,
6206 apr_pool_t *pool)
6207 {
6208 svn_client__merge_path_t *parent;
6209 const char *parent_abspath;
6210 apr_pool_t *iterpool;
6211 const apr_array_header_t *children;
6212 int i;
6213
6214 if (!(child->absent
6215 || (child->switched
6216 && strcmp(target->abspath,
6217 child->abspath) != 0)))
6218 return SVN_NO_ERROR;
6219
6220 parent_abspath = svn_dirent_dirname(child->abspath, pool);
6221 parent = get_child_with_mergeinfo(children_with_mergeinfo, parent_abspath);
6222 if (parent)
6223 {
6224 parent->missing_child = child->absent;
6225 parent->switched_child = child->switched;
6226 }
6227 else
6228 {
6229 /* Create a new element to insert into CHILDREN_WITH_MERGEINFO. */
6230 parent = svn_client__merge_path_create(parent_abspath, pool);
6231 parent->missing_child = child->absent;
6232 parent->switched_child = child->switched;
6233 /* Insert PARENT into CHILDREN_WITH_MERGEINFO. */
6234 SVN_ERR(insert_child_to_merge(children_with_mergeinfo, parent, pool));
6235 /* Increment for loop index so we don't process the inserted element. */
6236 (*curr_index)++;
6237 } /*(parent == NULL) */
6238
6239 /* Add all of PARENT's non-missing children that are not already present.*/
6240 SVN_ERR(svn_wc__node_get_children_of_working_node(&children, ctx->wc_ctx,
6241 parent_abspath,
6242 pool, pool));
6243 iterpool = svn_pool_create(pool);
6244 for (i = 0; i < children->nelts; i++)
6245 {
6246 const char *child_abspath = APR_ARRAY_IDX(children, i, const char *);
6247 svn_client__merge_path_t *sibling_of_missing;
6248
6249 svn_pool_clear(iterpool);
6250
6251 /* Does this child already exist in CHILDREN_WITH_MERGEINFO? */
6252 sibling_of_missing = get_child_with_mergeinfo(children_with_mergeinfo,
6253 child_abspath);
6254 /* Create the missing child and insert it into CHILDREN_WITH_MERGEINFO.*/
6255 if (!sibling_of_missing)
6256 {
6257 /* Don't add directory children if DEPTH is svn_depth_files. */
6258 if (depth == svn_depth_files)
6259 {
6260 svn_node_kind_t child_kind;
6261
6262 SVN_ERR(svn_wc_read_kind2(&child_kind,
6263 ctx->wc_ctx, child_abspath,
6264 FALSE, FALSE, iterpool));
6265 if (child_kind != svn_node_file)
6266 continue;
6267 }
6268
6269 sibling_of_missing = svn_client__merge_path_create(child_abspath,
6270 pool);
6271 SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
6272 sibling_of_missing, pool));
6273 }
6274 }
6275
6276 svn_pool_destroy(iterpool);
6277
6278 return SVN_NO_ERROR;
6279 }
6280
6281 /* pre_merge_status_cb's baton */
6282 struct pre_merge_status_baton_t
6283 {
6284 svn_wc_context_t *wc_ctx;
6285
6286 /* const char *absolute_wc_path to svn_depth_t * mapping for depths
6287 of empty, immediates, and files. */
6288 apr_hash_t *shallow_subtrees;
6289
6290 /* const char *absolute_wc_path to the same, for all paths missing
6291 from the working copy. */
6292 apr_hash_t *missing_subtrees;
6293
6294 /* const char *absolute_wc_path const char * repos relative path, describing
6295 the root of each switched subtree in the working copy and the repository
6296 relative path it is switched to. */
6297 apr_hash_t *switched_subtrees;
6298
6299 /* A pool to allocate additions to the above hashes in. */
6300 apr_pool_t *pool;
6301 };
6302
6303 /* A svn_wc_status_func4_t callback used by get_mergeinfo_paths to gather
6304 all switched, depth filtered and missing subtrees under a merge target.
6305
6306 Note that this doesn't see server and user excluded trees. */
6307 static svn_error_t *
pre_merge_status_cb(void * baton,const char * local_abspath,const svn_wc_status3_t * status,apr_pool_t * scratch_pool)6308 pre_merge_status_cb(void *baton,
6309 const char *local_abspath,
6310 const svn_wc_status3_t *status,
6311 apr_pool_t *scratch_pool)
6312 {
6313 struct pre_merge_status_baton_t *pmsb = baton;
6314
6315 if (status->switched && !status->file_external)
6316 {
6317 store_path(pmsb->switched_subtrees, local_abspath);
6318 }
6319
6320 if (status->depth == svn_depth_empty
6321 || status->depth == svn_depth_files)
6322 {
6323 const char *dup_abspath;
6324 svn_depth_t *depth = apr_pmemdup(pmsb->pool, &status->depth,
6325 sizeof *depth);
6326
6327 dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
6328
6329 svn_hash_sets(pmsb->shallow_subtrees, dup_abspath, depth);
6330 }
6331
6332 if (status->node_status == svn_wc_status_missing)
6333 {
6334 svn_boolean_t new_missing_root = TRUE;
6335 apr_hash_index_t *hi;
6336
6337 for (hi = apr_hash_first(scratch_pool, pmsb->missing_subtrees);
6338 hi;
6339 hi = apr_hash_next(hi))
6340 {
6341 const char *missing_root_path = apr_hash_this_key(hi);
6342
6343 if (svn_dirent_is_ancestor(missing_root_path,
6344 local_abspath))
6345 {
6346 new_missing_root = FALSE;
6347 break;
6348 }
6349 }
6350
6351 if (new_missing_root)
6352 store_path(pmsb->missing_subtrees, local_abspath);
6353 }
6354
6355 return SVN_NO_ERROR;
6356 }
6357
6358 /* Find all the subtrees in the working copy tree rooted at TARGET_ABSPATH
6359 * that have explicit mergeinfo.
6360 * Set *SUBTREES_WITH_MERGEINFO to a hash mapping (const char *) absolute
6361 * WC path to (svn_mergeinfo_t *) mergeinfo.
6362 *
6363 * ### Is this function equivalent to:
6364 *
6365 * svn_client__get_wc_mergeinfo_catalog(
6366 * subtrees_with_mergeinfo, inherited=NULL, include_descendants=TRUE,
6367 * svn_mergeinfo_explicit, target_abspath, limit_path=NULL,
6368 * walked_path=NULL, ignore_invalid_mergeinfo=FALSE, ...)
6369 *
6370 * except for the catalog keys being abspaths instead of repo-relpaths?
6371 */
6372 static svn_error_t *
get_wc_explicit_mergeinfo_catalog(apr_hash_t ** subtrees_with_mergeinfo,const char * target_abspath,svn_depth_t depth,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)6373 get_wc_explicit_mergeinfo_catalog(apr_hash_t **subtrees_with_mergeinfo,
6374 const char *target_abspath,
6375 svn_depth_t depth,
6376 svn_client_ctx_t *ctx,
6377 apr_pool_t *result_pool,
6378 apr_pool_t *scratch_pool)
6379 {
6380 svn_opt_revision_t working_revision = { svn_opt_revision_working, { 0 } };
6381 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6382 apr_hash_index_t *hi;
6383 apr_hash_t *externals;
6384
6385 SVN_ERR(svn_client_propget5(subtrees_with_mergeinfo, NULL,
6386 SVN_PROP_MERGEINFO, target_abspath,
6387 &working_revision, &working_revision, NULL,
6388 depth, NULL, ctx, result_pool, scratch_pool));
6389
6390 SVN_ERR(svn_wc__externals_defined_below(&externals, ctx->wc_ctx,
6391 target_abspath, scratch_pool,
6392 scratch_pool));
6393
6394 /* Convert property values to svn_mergeinfo_t. */
6395 for (hi = apr_hash_first(scratch_pool, *subtrees_with_mergeinfo);
6396 hi;
6397 hi = apr_hash_next(hi))
6398 {
6399 const char *wc_path = apr_hash_this_key(hi);
6400 svn_string_t *mergeinfo_string = apr_hash_this_val(hi);
6401 svn_mergeinfo_t mergeinfo;
6402 svn_error_t *err;
6403
6404 /* svn_client_propget5 picks up file externals with
6405 mergeinfo, but we don't want those. */
6406 if (svn_hash_gets(externals, wc_path))
6407 {
6408 svn_hash_sets(*subtrees_with_mergeinfo, wc_path, NULL);
6409 continue;
6410 }
6411
6412 svn_pool_clear(iterpool);
6413
6414 err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_string->data,
6415 result_pool);
6416 if (err)
6417 {
6418 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
6419 {
6420 err = svn_error_createf(
6421 SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
6422 _("Invalid mergeinfo detected on '%s', "
6423 "merge tracking not possible"),
6424 svn_dirent_local_style(wc_path, scratch_pool));
6425 }
6426 return svn_error_trace(err);
6427 }
6428 svn_hash_sets(*subtrees_with_mergeinfo, wc_path, mergeinfo);
6429 }
6430 svn_pool_destroy(iterpool);
6431
6432 return SVN_NO_ERROR;
6433 }
6434
6435 /* Helper for do_directory_merge() when performing merge-tracking aware
6436 merges.
6437
6438 Walk of the working copy tree rooted at TARGET->abspath to
6439 depth DEPTH. Create an svn_client__merge_path_t * for any path which meets
6440 one or more of the following criteria:
6441
6442 1) Path has working svn:mergeinfo.
6443 2) Path is switched.
6444 3) Path is a subtree of the merge target (i.e. is not equal to
6445 TARGET->abspath) and has no mergeinfo of its own but
6446 its immediate parent has mergeinfo with non-inheritable ranges. If
6447 this isn't a dry-run and the merge is between differences in the same
6448 repository, then this function will set working mergeinfo on the path
6449 equal to the mergeinfo inheritable from its parent.
6450 4) Path has an immediate child (or children) missing from the WC because
6451 the child is switched or absent from the WC, or due to a sparse
6452 checkout.
6453 5) Path has a sibling (or siblings) missing from the WC because the
6454 sibling is switched, absent, scheduled for deletion, or missing due to
6455 a sparse checkout.
6456 6) Path is absent from disk due to an authz restriction.
6457 7) Path is equal to TARGET->abspath.
6458 8) Path is an immediate *directory* child of
6459 TARGET->abspath and DEPTH is svn_depth_immediates.
6460 9) Path is an immediate *file* child of TARGET->abspath
6461 and DEPTH is svn_depth_files.
6462 10) Path is at a depth of 'empty' or 'files'.
6463 11) Path is missing from disk (e.g. due to an OS-level deletion).
6464
6465 If subtrees within the requested DEPTH are unexpectedly missing disk,
6466 then raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE.
6467
6468 Store the svn_client__merge_path_t *'s in *CHILDREN_WITH_MERGEINFO in
6469 depth-first order based on the svn_client__merge_path_t *s path member as
6470 sorted by svn_path_compare_paths(). Set the remaining_ranges field of each
6471 element to NULL.
6472
6473 Note: Since the walk is rooted at TARGET->abspath, the
6474 latter is guaranteed to be in *CHILDREN_WITH_MERGEINFO and due to the
6475 depth-first ordering it is guaranteed to be the first element in
6476 *CHILDREN_WITH_MERGEINFO.
6477
6478 MERGE_CMD_BATON is cascaded from the argument of the same name in
6479 do_directory_merge().
6480 */
6481 static svn_error_t *
get_mergeinfo_paths(apr_array_header_t * children_with_mergeinfo,const merge_target_t * target,svn_depth_t depth,svn_boolean_t dry_run,svn_boolean_t same_repos,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)6482 get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
6483 const merge_target_t *target,
6484 svn_depth_t depth,
6485 svn_boolean_t dry_run,
6486 svn_boolean_t same_repos,
6487 svn_client_ctx_t *ctx,
6488 apr_pool_t *result_pool,
6489 apr_pool_t *scratch_pool)
6490 {
6491 int i;
6492 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6493 apr_pool_t *swmi_pool;
6494 apr_hash_t *subtrees_with_mergeinfo;
6495 apr_hash_t *excluded_subtrees;
6496 apr_hash_t *switched_subtrees;
6497 apr_hash_t *shallow_subtrees;
6498 apr_hash_t *missing_subtrees;
6499 struct pre_merge_status_baton_t pre_merge_status_baton;
6500
6501 /* Case 1: Subtrees with explicit mergeinfo. */
6502 /* Use a subpool for subtrees_with_mergeinfo, as it can be very large
6503 and is temporary. */
6504 swmi_pool = svn_pool_create(scratch_pool);
6505 SVN_ERR(get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
6506 target->abspath,
6507 depth, ctx,
6508 swmi_pool, swmi_pool));
6509 if (subtrees_with_mergeinfo)
6510 {
6511 apr_hash_index_t *hi;
6512
6513 for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
6514 hi;
6515 hi = apr_hash_next(hi))
6516 {
6517 const char *wc_path = apr_hash_this_key(hi);
6518 svn_mergeinfo_t mergeinfo = apr_hash_this_val(hi);
6519 svn_client__merge_path_t *mergeinfo_child =
6520 svn_client__merge_path_create(wc_path, result_pool);
6521
6522 svn_pool_clear(iterpool);
6523
6524 /* Stash this child's pre-existing mergeinfo. */
6525 mergeinfo_child->pre_merge_mergeinfo = mergeinfo;
6526
6527 /* Note if this child has non-inheritable mergeinfo */
6528 mergeinfo_child->has_noninheritable
6529 = svn_mergeinfo__is_noninheritable(
6530 mergeinfo_child->pre_merge_mergeinfo, iterpool);
6531
6532 /* Append it. We'll sort below. */
6533 APR_ARRAY_PUSH(children_with_mergeinfo, svn_client__merge_path_t *)
6534 = svn_client__merge_path_dup(mergeinfo_child, result_pool);
6535 }
6536
6537 /* Sort CHILDREN_WITH_MERGEINFO by each child's path (i.e. as per
6538 compare_merge_path_t_as_paths). Any subsequent insertions of new
6539 children with insert_child_to_merge() require this ordering. */
6540 svn_sort__array(children_with_mergeinfo, compare_merge_path_t_as_paths);
6541 }
6542 svn_pool_destroy(swmi_pool);
6543
6544 /* Case 2: Switched subtrees
6545 Case 10: Paths at depths of 'empty' or 'files'
6546 Case 11: Paths missing from disk */
6547 pre_merge_status_baton.wc_ctx = ctx->wc_ctx;
6548 switched_subtrees = apr_hash_make(scratch_pool);
6549 pre_merge_status_baton.switched_subtrees = switched_subtrees;
6550 shallow_subtrees = apr_hash_make(scratch_pool);
6551 pre_merge_status_baton.shallow_subtrees = shallow_subtrees;
6552 missing_subtrees = apr_hash_make(scratch_pool);
6553 pre_merge_status_baton.missing_subtrees = missing_subtrees;
6554 pre_merge_status_baton.pool = scratch_pool;
6555 SVN_ERR(svn_wc_walk_status(ctx->wc_ctx,
6556 target->abspath,
6557 depth,
6558 TRUE /* get_all */,
6559 FALSE /* no_ignore */,
6560 TRUE /* ignore_text_mods */,
6561 NULL /* ingore_patterns */,
6562 pre_merge_status_cb, &pre_merge_status_baton,
6563 ctx->cancel_func, ctx->cancel_baton,
6564 scratch_pool));
6565
6566 /* Issue #2915: Raise an error describing the roots of any missing
6567 subtrees, i.e. those that the WC thinks are on disk but have been
6568 removed outside of Subversion. */
6569 if (apr_hash_count(missing_subtrees))
6570 {
6571 apr_hash_index_t *hi;
6572 svn_stringbuf_t *missing_subtree_err_buf =
6573 svn_stringbuf_create(_("Merge tracking not allowed with missing "
6574 "subtrees; try restoring these items "
6575 "first:\n"), scratch_pool);
6576
6577 for (hi = apr_hash_first(scratch_pool, missing_subtrees);
6578 hi;
6579 hi = apr_hash_next(hi))
6580 {
6581 svn_pool_clear(iterpool);
6582 svn_stringbuf_appendcstr(missing_subtree_err_buf,
6583 svn_dirent_local_style(
6584 apr_hash_this_key(hi), iterpool));
6585 svn_stringbuf_appendcstr(missing_subtree_err_buf, "\n");
6586 }
6587
6588 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
6589 NULL, missing_subtree_err_buf->data);
6590 }
6591
6592 if (apr_hash_count(switched_subtrees))
6593 {
6594 apr_hash_index_t *hi;
6595
6596 for (hi = apr_hash_first(scratch_pool, switched_subtrees);
6597 hi;
6598 hi = apr_hash_next(hi))
6599 {
6600 const char *wc_path = apr_hash_this_key(hi);
6601 svn_client__merge_path_t *child = get_child_with_mergeinfo(
6602 children_with_mergeinfo, wc_path);
6603
6604 if (child)
6605 {
6606 child->switched = TRUE;
6607 }
6608 else
6609 {
6610 svn_client__merge_path_t *switched_child =
6611 svn_client__merge_path_create(wc_path, result_pool);
6612 switched_child->switched = TRUE;
6613 SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
6614 switched_child, result_pool));
6615 }
6616 }
6617 }
6618
6619 if (apr_hash_count(shallow_subtrees))
6620 {
6621 apr_hash_index_t *hi;
6622
6623 for (hi = apr_hash_first(scratch_pool, shallow_subtrees);
6624 hi;
6625 hi = apr_hash_next(hi))
6626 {
6627 svn_boolean_t new_shallow_child = FALSE;
6628 const char *wc_path = apr_hash_this_key(hi);
6629 svn_depth_t *child_depth = apr_hash_this_val(hi);
6630 svn_client__merge_path_t *shallow_child = get_child_with_mergeinfo(
6631 children_with_mergeinfo, wc_path);
6632
6633 if (shallow_child)
6634 {
6635 if (*child_depth == svn_depth_empty
6636 || *child_depth == svn_depth_files)
6637 shallow_child->missing_child = TRUE;
6638 }
6639 else
6640 {
6641 shallow_child = svn_client__merge_path_create(wc_path,
6642 result_pool);
6643 new_shallow_child = TRUE;
6644
6645 if (*child_depth == svn_depth_empty
6646 || *child_depth == svn_depth_files)
6647 shallow_child->missing_child = TRUE;
6648 }
6649
6650 /* A little trickery: If PATH doesn't have any mergeinfo or has
6651 only inheritable mergeinfo, we still describe it as having
6652 non-inheritable mergeinfo if it is missing a child due to
6653 a shallow depth. Why? Because the mergeinfo we'll add to PATH
6654 to describe the merge must be non-inheritable, so PATH's missing
6655 children don't inherit it. Marking these PATHs as non-
6656 inheritable allows the logic for case 3 to properly account
6657 for PATH's children. */
6658 if (!shallow_child->has_noninheritable
6659 && (*child_depth == svn_depth_empty
6660 || *child_depth == svn_depth_files))
6661 {
6662 shallow_child->has_noninheritable = TRUE;
6663 }
6664
6665 if (new_shallow_child)
6666 SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
6667 shallow_child, result_pool));
6668 }
6669 }
6670
6671 /* Case 6: Paths absent from disk due to server or user exclusion. */
6672 SVN_ERR(svn_wc__get_excluded_subtrees(&excluded_subtrees,
6673 ctx->wc_ctx, target->abspath,
6674 result_pool, scratch_pool));
6675 if (excluded_subtrees)
6676 {
6677 apr_hash_index_t *hi;
6678
6679 for (hi = apr_hash_first(scratch_pool, excluded_subtrees);
6680 hi;
6681 hi = apr_hash_next(hi))
6682 {
6683 const char *wc_path = apr_hash_this_key(hi);
6684 svn_client__merge_path_t *child = get_child_with_mergeinfo(
6685 children_with_mergeinfo, wc_path);
6686
6687 if (child)
6688 {
6689 child->absent = TRUE;
6690 }
6691 else
6692 {
6693 svn_client__merge_path_t *absent_child =
6694 svn_client__merge_path_create(wc_path, result_pool);
6695 absent_child->absent = TRUE;
6696 SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
6697 absent_child, result_pool));
6698 }
6699 }
6700 }
6701
6702 /* Case 7: The merge target MERGE_CMD_BATON->target->abspath is always
6703 present. */
6704 if (!get_child_with_mergeinfo(children_with_mergeinfo,
6705 target->abspath))
6706 {
6707 svn_client__merge_path_t *target_child =
6708 svn_client__merge_path_create(target->abspath,
6709 result_pool);
6710 SVN_ERR(insert_child_to_merge(children_with_mergeinfo, target_child,
6711 result_pool));
6712 }
6713
6714 /* Case 8: Path is an immediate *directory* child of
6715 MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_immediates.
6716
6717 Case 9: Path is an immediate *file* child of
6718 MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_files. */
6719 if (depth == svn_depth_immediates || depth == svn_depth_files)
6720 {
6721 int j;
6722 const apr_array_header_t *immediate_children;
6723
6724 SVN_ERR(svn_wc__node_get_children_of_working_node(
6725 &immediate_children, ctx->wc_ctx,
6726 target->abspath, scratch_pool, scratch_pool));
6727
6728 for (j = 0; j < immediate_children->nelts; j++)
6729 {
6730 const char *immediate_child_abspath =
6731 APR_ARRAY_IDX(immediate_children, j, const char *);
6732 svn_node_kind_t immediate_child_kind;
6733
6734 svn_pool_clear(iterpool);
6735 SVN_ERR(svn_wc_read_kind2(&immediate_child_kind,
6736 ctx->wc_ctx, immediate_child_abspath,
6737 FALSE, FALSE, iterpool));
6738 if ((immediate_child_kind == svn_node_dir
6739 && depth == svn_depth_immediates)
6740 || (immediate_child_kind == svn_node_file
6741 && depth == svn_depth_files))
6742 {
6743 if (!get_child_with_mergeinfo(children_with_mergeinfo,
6744 immediate_child_abspath))
6745 {
6746 svn_client__merge_path_t *immediate_child =
6747 svn_client__merge_path_create(immediate_child_abspath,
6748 result_pool);
6749
6750 if (immediate_child_kind == svn_node_dir
6751 && depth == svn_depth_immediates)
6752 immediate_child->immediate_child_dir = TRUE;
6753
6754 SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
6755 immediate_child, result_pool));
6756 }
6757 }
6758 }
6759 }
6760
6761 /* If DEPTH isn't empty then cover cases 3), 4), and 5), possibly adding
6762 elements to CHILDREN_WITH_MERGEINFO. */
6763 if (depth <= svn_depth_empty)
6764 return SVN_NO_ERROR;
6765
6766 for (i = 0; i < children_with_mergeinfo->nelts; i++)
6767 {
6768 svn_client__merge_path_t *child =
6769 APR_ARRAY_IDX(children_with_mergeinfo, i,
6770 svn_client__merge_path_t *);
6771 svn_pool_clear(iterpool);
6772
6773 /* Case 3) Where merging to a path with a switched child the path
6774 gets non-inheritable mergeinfo for the merge range performed and
6775 the child gets its own set of mergeinfo. If the switched child
6776 later "returns", e.g. a switched path is unswitched, the child
6777 may not have any explicit mergeinfo. If the initial merge is
6778 repeated we don't want to repeat the merge for the path, but we
6779 do want to repeat it for the previously switched child. To
6780 ensure this we check if all of CHILD's non-missing children have
6781 explicit mergeinfo (they should already be present in
6782 CHILDREN_WITH_MERGEINFO if they do). If not,
6783 add the children without mergeinfo to CHILDREN_WITH_MERGEINFO so
6784 do_directory_merge() will merge them independently.
6785
6786 But that's not enough! Since do_directory_merge() performs
6787 the merges on the paths in CHILDREN_WITH_MERGEINFO in a depth
6788 first manner it will merge the previously switched path's parent
6789 first. As part of this merge it will update the parent's
6790 previously non-inheritable mergeinfo and make it inheritable
6791 (since it notices the path has no missing children), then when
6792 do_directory_merge() finally merges the previously missing
6793 child it needs to get mergeinfo from the child's nearest
6794 ancestor, but since do_directory_merge() already tweaked that
6795 mergeinfo, removing the non-inheritable flag, it appears that the
6796 child already has been merged to. To prevent this we set
6797 override mergeinfo on the child now, before any merging is done,
6798 so it has explicit mergeinfo that reflects only CHILD's
6799 inheritable mergeinfo. */
6800
6801 /* If depth is immediates or files then don't add new children if
6802 CHILD is a subtree of the merge target; those children are below
6803 the operational depth of the merge. */
6804 if (child->has_noninheritable
6805 && (i == 0 || depth == svn_depth_infinity))
6806 {
6807 const apr_array_header_t *children;
6808 int j;
6809
6810 SVN_ERR(svn_wc__node_get_children_of_working_node(
6811 &children,
6812 ctx->wc_ctx,
6813 child->abspath,
6814 iterpool, iterpool));
6815 for (j = 0; j < children->nelts; j++)
6816 {
6817 svn_client__merge_path_t *child_of_noninheritable;
6818 const char *child_abspath = APR_ARRAY_IDX(children, j,
6819 const char*);
6820
6821 /* Does this child already exist in CHILDREN_WITH_MERGEINFO?
6822 If not, create it and insert it into
6823 CHILDREN_WITH_MERGEINFO and set override mergeinfo on
6824 it. */
6825 child_of_noninheritable =
6826 get_child_with_mergeinfo(children_with_mergeinfo,
6827 child_abspath);
6828 if (!child_of_noninheritable)
6829 {
6830 /* Don't add directory children if DEPTH
6831 is svn_depth_files. */
6832 if (depth == svn_depth_files)
6833 {
6834 svn_node_kind_t child_kind;
6835 SVN_ERR(svn_wc_read_kind2(&child_kind,
6836 ctx->wc_ctx, child_abspath,
6837 FALSE, FALSE, iterpool));
6838 if (child_kind != svn_node_file)
6839 continue;
6840 }
6841 /* else DEPTH is infinity or immediates so we want both
6842 directory and file children. */
6843
6844 child_of_noninheritable =
6845 svn_client__merge_path_create(child_abspath, result_pool);
6846 child_of_noninheritable->child_of_noninheritable = TRUE;
6847 SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
6848 child_of_noninheritable,
6849 result_pool));
6850 if (!dry_run && same_repos)
6851 {
6852 svn_mergeinfo_t mergeinfo;
6853
6854 SVN_ERR(svn_client__get_wc_mergeinfo(
6855 &mergeinfo, NULL,
6856 svn_mergeinfo_nearest_ancestor,
6857 child_of_noninheritable->abspath,
6858 target->abspath, NULL, FALSE,
6859 ctx, iterpool, iterpool));
6860
6861 SVN_ERR(svn_client__record_wc_mergeinfo(
6862 child_of_noninheritable->abspath, mergeinfo,
6863 FALSE, ctx, iterpool));
6864 }
6865 }
6866 }
6867 }
6868 /* Case 4 and 5 are handled by the following function. */
6869 SVN_ERR(insert_parent_and_sibs_of_sw_absent_del_subtree(
6870 children_with_mergeinfo, target, &i, child,
6871 depth, ctx, result_pool));
6872 } /* i < children_with_mergeinfo->nelts */
6873 svn_pool_destroy(iterpool);
6874
6875 return SVN_NO_ERROR;
6876 }
6877
6878
6879 /* Implements the svn_log_entry_receiver_t interface.
6880 *
6881 * BATON is an 'apr_array_header_t *' array of 'svn_revnum_t'.
6882 * Push a copy of LOG_ENTRY->revision onto BATON. Thus, a
6883 * series of invocations of this callback accumulates the
6884 * corresponding set of revisions into BATON.
6885 */
6886 static svn_error_t *
log_changed_revs(void * baton,svn_log_entry_t * log_entry,apr_pool_t * pool)6887 log_changed_revs(void *baton,
6888 svn_log_entry_t *log_entry,
6889 apr_pool_t *pool)
6890 {
6891 apr_array_header_t *revs = baton;
6892
6893 APR_ARRAY_PUSH(revs, svn_revnum_t) = log_entry->revision;
6894 return SVN_NO_ERROR;
6895 }
6896
6897
6898 /* Set *MIN_REV_P to the oldest and *MAX_REV_P to the youngest start or end
6899 * revision occurring in RANGELIST, or to SVN_INVALID_REVNUM if RANGELIST
6900 * is empty. */
6901 static void
merge_range_find_extremes(svn_revnum_t * min_rev_p,svn_revnum_t * max_rev_p,const svn_rangelist_t * rangelist)6902 merge_range_find_extremes(svn_revnum_t *min_rev_p,
6903 svn_revnum_t *max_rev_p,
6904 const svn_rangelist_t *rangelist)
6905 {
6906 int i;
6907
6908 *min_rev_p = SVN_INVALID_REVNUM;
6909 *max_rev_p = SVN_INVALID_REVNUM;
6910 for (i = 0; i < rangelist->nelts; i++)
6911 {
6912 svn_merge_range_t *range
6913 = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
6914 svn_revnum_t range_min = MIN(range->start, range->end);
6915 svn_revnum_t range_max = MAX(range->start, range->end);
6916
6917 if ((! SVN_IS_VALID_REVNUM(*min_rev_p)) || (range_min < *min_rev_p))
6918 *min_rev_p = range_min;
6919 if ((! SVN_IS_VALID_REVNUM(*max_rev_p)) || (range_max > *max_rev_p))
6920 *max_rev_p = range_max;
6921 }
6922 }
6923
6924 /* Wrapper around svn_ra_get_log2(). Invoke RECEIVER with RECEIVER_BATON
6925 * on each commit from YOUNGEST_REV to OLDEST_REV in which TARGET_RELPATH
6926 * changed. TARGET_RELPATH is relative to RA_SESSION's URL.
6927 * Important: Revision properties are not retrieved by this function for
6928 * performance reasons.
6929 */
6930 static svn_error_t *
get_log(svn_ra_session_t * ra_session,const char * target_relpath,svn_revnum_t youngest_rev,svn_revnum_t oldest_rev,svn_boolean_t discover_changed_paths,svn_log_entry_receiver_t receiver,void * receiver_baton,apr_pool_t * pool)6931 get_log(svn_ra_session_t *ra_session,
6932 const char *target_relpath,
6933 svn_revnum_t youngest_rev,
6934 svn_revnum_t oldest_rev,
6935 svn_boolean_t discover_changed_paths,
6936 svn_log_entry_receiver_t receiver,
6937 void *receiver_baton,
6938 apr_pool_t *pool)
6939 {
6940 apr_array_header_t *log_targets;
6941 apr_array_header_t *revprops;
6942
6943 log_targets = apr_array_make(pool, 1, sizeof(const char *));
6944 APR_ARRAY_PUSH(log_targets, const char *) = target_relpath;
6945
6946 revprops = apr_array_make(pool, 0, sizeof(const char *));
6947
6948 SVN_ERR(svn_ra_get_log2(ra_session, log_targets, youngest_rev,
6949 oldest_rev, 0 /* limit */, discover_changed_paths,
6950 FALSE /* strict_node_history */,
6951 FALSE /* include_merged_revisions */,
6952 revprops, receiver, receiver_baton, pool));
6953
6954 return SVN_NO_ERROR;
6955 }
6956
6957 /* Set *OPERATIVE_RANGES_P to an array of svn_merge_range_t * merge
6958 range objects copied wholesale from RANGES which have the property
6959 that in some revision within that range the object identified by
6960 RA_SESSION was modified (if by "modified" we mean "'svn log' would
6961 return that revision). *OPERATIVE_RANGES_P is allocated from the
6962 same pool as RANGES, and the ranges within it are shared with
6963 RANGES, too.
6964
6965 *OPERATIVE_RANGES_P may be the same as RANGES (that is, the output
6966 parameter is set only after the input is no longer used).
6967
6968 Use POOL for temporary allocations. */
6969 static svn_error_t *
remove_noop_merge_ranges(svn_rangelist_t ** operative_ranges_p,svn_ra_session_t * ra_session,const svn_rangelist_t * ranges,apr_pool_t * pool)6970 remove_noop_merge_ranges(svn_rangelist_t **operative_ranges_p,
6971 svn_ra_session_t *ra_session,
6972 const svn_rangelist_t *ranges,
6973 apr_pool_t *pool)
6974 {
6975 int i;
6976 svn_revnum_t oldest_rev, youngest_rev;
6977 apr_array_header_t *changed_revs =
6978 apr_array_make(pool, ranges->nelts, sizeof(svn_revnum_t));
6979 svn_rangelist_t *operative_ranges =
6980 apr_array_make(ranges->pool, ranges->nelts, ranges->elt_size);
6981
6982 /* Find the revision extremes of the RANGES we have. */
6983 merge_range_find_extremes(&oldest_rev, &youngest_rev, ranges);
6984 if (SVN_IS_VALID_REVNUM(oldest_rev))
6985 oldest_rev++; /* make it inclusive */
6986
6987 /* Get logs across those ranges, recording which revisions hold
6988 changes to our object's history. */
6989 SVN_ERR(get_log(ra_session, "", youngest_rev, oldest_rev, FALSE,
6990 log_changed_revs, changed_revs, pool));
6991
6992 /* Are there *any* changes? */
6993 if (changed_revs->nelts)
6994 {
6995 /* Our list of changed revisions should be in youngest-to-oldest
6996 order. */
6997 svn_revnum_t youngest_changed_rev
6998 = APR_ARRAY_IDX(changed_revs, 0, svn_revnum_t);
6999 svn_revnum_t oldest_changed_rev
7000 = APR_ARRAY_IDX(changed_revs, changed_revs->nelts - 1, svn_revnum_t);
7001
7002 /* Now, copy from RANGES to *OPERATIVE_RANGES, filtering out ranges
7003 that aren't operative (by virtue of not having any revisions
7004 represented in the CHANGED_REVS array). */
7005 for (i = 0; i < ranges->nelts; i++)
7006 {
7007 svn_merge_range_t *range = APR_ARRAY_IDX(ranges, i,
7008 svn_merge_range_t *);
7009 svn_revnum_t range_min = MIN(range->start, range->end) + 1;
7010 svn_revnum_t range_max = MAX(range->start, range->end);
7011 int j;
7012
7013 /* If the merge range is entirely outside the range of changed
7014 revisions, we've no use for it. */
7015 if ((range_min > youngest_changed_rev)
7016 || (range_max < oldest_changed_rev))
7017 continue;
7018
7019 /* Walk through the changed_revs to see if any of them fall
7020 inside our current range. */
7021 for (j = 0; j < changed_revs->nelts; j++)
7022 {
7023 svn_revnum_t changed_rev
7024 = APR_ARRAY_IDX(changed_revs, j, svn_revnum_t);
7025 if ((changed_rev >= range_min) && (changed_rev <= range_max))
7026 {
7027 APR_ARRAY_PUSH(operative_ranges, svn_merge_range_t *) =
7028 range;
7029 break;
7030 }
7031 }
7032 }
7033 }
7034
7035 *operative_ranges_p = operative_ranges;
7036 return SVN_NO_ERROR;
7037 }
7038
7039
7040 /*-----------------------------------------------------------------------*/
7041
7042 /*** Merge Source Normalization ***/
7043
7044 /* qsort-compatible sort routine, rating merge_source_t * objects to
7045 be in descending (youngest-to-oldest) order based on their ->loc1->rev
7046 component. */
7047 static int
compare_merge_source_ts(const void * a,const void * b)7048 compare_merge_source_ts(const void *a,
7049 const void *b)
7050 {
7051 svn_revnum_t a_rev = (*(const merge_source_t *const *)a)->loc1->rev;
7052 svn_revnum_t b_rev = (*(const merge_source_t *const *)b)->loc1->rev;
7053 if (a_rev == b_rev)
7054 return 0;
7055 return a_rev < b_rev ? 1 : -1;
7056 }
7057
7058 /* Set *MERGE_SOURCE_TS_P to a list of merge sources generated by
7059 slicing history location SEGMENTS with a given requested merge
7060 RANGE. Use SOURCE_LOC for full source URL calculation.
7061
7062 Order the merge sources in *MERGE_SOURCE_TS_P from oldest to
7063 youngest. */
7064 static svn_error_t *
combine_range_with_segments(apr_array_header_t ** merge_source_ts_p,const svn_merge_range_t * range,const apr_array_header_t * segments,const svn_client__pathrev_t * source_loc,apr_pool_t * pool)7065 combine_range_with_segments(apr_array_header_t **merge_source_ts_p,
7066 const svn_merge_range_t *range,
7067 const apr_array_header_t *segments,
7068 const svn_client__pathrev_t *source_loc,
7069 apr_pool_t *pool)
7070 {
7071 apr_array_header_t *merge_source_ts =
7072 apr_array_make(pool, 1, sizeof(merge_source_t *));
7073 svn_revnum_t minrev = MIN(range->start, range->end) + 1;
7074 svn_revnum_t maxrev = MAX(range->start, range->end);
7075 svn_boolean_t subtractive = (range->start > range->end);
7076 int i;
7077
7078 for (i = 0; i < segments->nelts; i++)
7079 {
7080 svn_location_segment_t *segment =
7081 APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
7082 svn_client__pathrev_t *loc1, *loc2;
7083 merge_source_t *merge_source;
7084 const char *path1 = NULL;
7085 svn_revnum_t rev1;
7086
7087 /* If this segment doesn't overlap our range at all, or
7088 represents a gap, ignore it. */
7089 if ((segment->range_end < minrev)
7090 || (segment->range_start > maxrev)
7091 || (! segment->path))
7092 continue;
7093
7094 /* If our range spans a segment boundary, we have to point our
7095 merge_source_t's path1 to the path of the immediately older
7096 segment, else it points to the same location as its path2. */
7097 rev1 = MAX(segment->range_start, minrev) - 1;
7098 if (minrev <= segment->range_start)
7099 {
7100 if (i > 0)
7101 {
7102 path1 = (APR_ARRAY_IDX(segments, i - 1,
7103 svn_location_segment_t *))->path;
7104 }
7105 /* If we've backed PATH1 up into a segment gap, let's back
7106 it up further still to the segment before the gap. We'll
7107 have to adjust rev1, too. */
7108 if ((! path1) && (i > 1))
7109 {
7110 path1 = (APR_ARRAY_IDX(segments, i - 2,
7111 svn_location_segment_t *))->path;
7112 rev1 = (APR_ARRAY_IDX(segments, i - 2,
7113 svn_location_segment_t *))->range_end;
7114 }
7115 }
7116 else
7117 {
7118 path1 = apr_pstrdup(pool, segment->path);
7119 }
7120
7121 /* If we don't have two valid paths, we won't know what to do
7122 when merging. This could happen if someone requested a merge
7123 where the source didn't exist in a particular revision or
7124 something. The merge code would probably bomb out anyway, so
7125 we'll just *not* create a merge source in this case. */
7126 if (! (path1 && segment->path))
7127 continue;
7128
7129 /* Build our merge source structure. */
7130 loc1 = svn_client__pathrev_create_with_relpath(
7131 source_loc->repos_root_url, source_loc->repos_uuid,
7132 rev1, path1, pool);
7133 loc2 = svn_client__pathrev_create_with_relpath(
7134 source_loc->repos_root_url, source_loc->repos_uuid,
7135 MIN(segment->range_end, maxrev), segment->path, pool);
7136 /* If this is subtractive, reverse the whole calculation. */
7137 if (subtractive)
7138 merge_source = merge_source_create(loc2, loc1, TRUE /* ancestral */,
7139 pool);
7140 else
7141 merge_source = merge_source_create(loc1, loc2, TRUE /* ancestral */,
7142 pool);
7143
7144 APR_ARRAY_PUSH(merge_source_ts, merge_source_t *) = merge_source;
7145 }
7146
7147 /* If this was a subtractive merge, and we created more than one
7148 merge source, we need to reverse the sort ordering of our sources. */
7149 if (subtractive && (merge_source_ts->nelts > 1))
7150 svn_sort__array(merge_source_ts, compare_merge_source_ts);
7151
7152 *merge_source_ts_p = merge_source_ts;
7153 return SVN_NO_ERROR;
7154 }
7155
7156 /* Similar to normalize_merge_sources() except the input MERGE_RANGE_TS is a
7157 * rangelist.
7158 */
7159 static svn_error_t *
normalize_merge_sources_internal(apr_array_header_t ** merge_sources_p,const svn_client__pathrev_t * source_loc,const svn_rangelist_t * merge_range_ts,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7160 normalize_merge_sources_internal(apr_array_header_t **merge_sources_p,
7161 const svn_client__pathrev_t *source_loc,
7162 const svn_rangelist_t *merge_range_ts,
7163 svn_ra_session_t *ra_session,
7164 svn_client_ctx_t *ctx,
7165 apr_pool_t *result_pool,
7166 apr_pool_t *scratch_pool)
7167 {
7168 svn_revnum_t source_peg_revnum = source_loc->rev;
7169 svn_revnum_t oldest_requested, youngest_requested;
7170 svn_revnum_t trim_revision = SVN_INVALID_REVNUM;
7171 apr_array_header_t *segments;
7172 int i;
7173
7174 /* Initialize our return variable. */
7175 *merge_sources_p = apr_array_make(result_pool, 1, sizeof(merge_source_t *));
7176
7177 /* No ranges to merge? No problem. */
7178 if (merge_range_ts->nelts == 0)
7179 return SVN_NO_ERROR;
7180
7181 /* Find the extremes of the revisions across our set of ranges. */
7182 merge_range_find_extremes(&oldest_requested, &youngest_requested,
7183 merge_range_ts);
7184
7185 /* ### FIXME: Our underlying APIs can't yet handle the case where
7186 the peg revision isn't the youngest of the three revisions. So
7187 we'll just verify that the source in the peg revision is related
7188 to the source in the youngest requested revision (which is
7189 all the underlying APIs would do in this case right now anyway). */
7190 if (source_peg_revnum < youngest_requested)
7191 {
7192 svn_client__pathrev_t *start_loc;
7193
7194 SVN_ERR(svn_client__repos_location(&start_loc,
7195 ra_session, source_loc,
7196 youngest_requested,
7197 ctx, scratch_pool, scratch_pool));
7198 source_peg_revnum = youngest_requested;
7199 }
7200
7201 /* Fetch the locations for our merge range span. */
7202 SVN_ERR(svn_client__repos_location_segments(&segments,
7203 ra_session, source_loc->url,
7204 source_peg_revnum,
7205 youngest_requested,
7206 oldest_requested,
7207 ctx, result_pool));
7208
7209 /* See if we fetched enough history to do the job. "Surely we did,"
7210 you say. "After all, we covered the entire requested merge
7211 range." Yes, that's true, but if our first segment doesn't
7212 extend back to the oldest request revision, we've got a special
7213 case to deal with. Or if the first segment represents a gap,
7214 that's another special case. */
7215 trim_revision = SVN_INVALID_REVNUM;
7216 if (segments->nelts)
7217 {
7218 svn_location_segment_t *first_segment =
7219 APR_ARRAY_IDX(segments, 0, svn_location_segment_t *);
7220
7221 /* If the first segment doesn't start with the OLDEST_REQUESTED
7222 revision, we'll need to pass a trim revision to our range
7223 cruncher. */
7224 if (first_segment->range_start != oldest_requested)
7225 {
7226 trim_revision = first_segment->range_start;
7227 }
7228
7229 /* Else, if the first segment has no path (and therefore is a
7230 gap), then we'll fetch the copy source revision from the
7231 second segment (provided there is one, of course) and use it
7232 to prepend an extra pathful segment to our list.
7233
7234 ### We could avoid this bit entirely if we'd passed
7235 ### SVN_INVALID_REVNUM instead of OLDEST_REQUESTED to
7236 ### svn_client__repos_location_segments(), but that would
7237 ### really penalize clients hitting pre-1.5 repositories with
7238 ### the typical small merge range request (because of the
7239 ### lack of a node-origins cache in the repository). */
7240 else if (! first_segment->path)
7241 {
7242 if (segments->nelts > 1)
7243 {
7244 svn_location_segment_t *second_segment =
7245 APR_ARRAY_IDX(segments, 1, svn_location_segment_t *);
7246 const char *segment_url;
7247 const char *original_repos_relpath;
7248 svn_revnum_t original_revision;
7249 svn_opt_revision_t range_start_rev;
7250 range_start_rev.kind = svn_opt_revision_number;
7251 range_start_rev.value.number = second_segment->range_start;
7252
7253 segment_url = svn_path_url_add_component2(
7254 source_loc->repos_root_url, second_segment->path,
7255 scratch_pool);
7256 SVN_ERR(svn_client__get_copy_source(&original_repos_relpath,
7257 &original_revision,
7258 segment_url,
7259 &range_start_rev,
7260 ra_session, ctx,
7261 result_pool, scratch_pool));
7262 /* Got copyfrom data? Fix up the first segment to cover
7263 back to COPYFROM_REV + 1, and then prepend a new
7264 segment covering just COPYFROM_REV. */
7265 if (original_repos_relpath)
7266 {
7267 svn_location_segment_t *new_segment =
7268 apr_pcalloc(result_pool, sizeof(*new_segment));
7269
7270 new_segment->path = original_repos_relpath;
7271 new_segment->range_start = original_revision;
7272 new_segment->range_end = original_revision;
7273 SVN_ERR(svn_sort__array_insert2(segments, &new_segment, 0));
7274 }
7275 }
7276 }
7277 }
7278
7279 /* For each range in our requested range set, try to determine the
7280 path(s) associated with that range. */
7281 for (i = 0; i < merge_range_ts->nelts; i++)
7282 {
7283 svn_merge_range_t *range =
7284 APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *);
7285 apr_array_header_t *merge_sources;
7286
7287 if (SVN_IS_VALID_REVNUM(trim_revision))
7288 {
7289 /* If the range predates the trim revision, discard it. */
7290 if (MAX(range->start, range->end) < trim_revision)
7291 continue;
7292
7293 /* If the range overlaps the trim revision, trim it. */
7294 if (range->start < trim_revision)
7295 range->start = trim_revision;
7296 if (range->end < trim_revision)
7297 range->end = trim_revision;
7298 }
7299
7300 /* Copy the resulting merge sources into master list thereof. */
7301 SVN_ERR(combine_range_with_segments(&merge_sources, range,
7302 segments, source_loc,
7303 result_pool));
7304 apr_array_cat(*merge_sources_p, merge_sources);
7305 }
7306
7307 return SVN_NO_ERROR;
7308 }
7309
7310 /* Determine the normalized ranges to merge from a given line of history.
7311
7312 Calculate the result by intersecting the list of location segments at
7313 which SOURCE_LOC existed along its line of history with the requested
7314 revision ranges in RANGES_TO_MERGE. RANGES_TO_MERGE is an array of
7315 (svn_opt_revision_range_t *) revision ranges. Use SOURCE_PATH_OR_URL to
7316 resolve any WC-relative revision specifiers (such as 'base') in
7317 RANGES_TO_MERGE.
7318
7319 Set *MERGE_SOURCES_P to an array of merge_source_t * objects, each
7320 describing a normalized range of revisions to be merged from the line
7321 history of SOURCE_LOC. Order the objects from oldest to youngest.
7322
7323 RA_SESSION is an RA session open to the repository of SOURCE_LOC; it may
7324 be temporarily reparented within this function. Use RA_SESSION to find
7325 the location segments along the line of history of SOURCE_LOC.
7326
7327 Allocate MERGE_SOURCES_P and its contents in RESULT_POOL.
7328
7329 See `MERGEINFO MERGE SOURCE NORMALIZATION' for more on the
7330 background of this function.
7331 */
7332 static svn_error_t *
normalize_merge_sources(apr_array_header_t ** merge_sources_p,const char * source_path_or_url,const svn_client__pathrev_t * source_loc,const apr_array_header_t * ranges_to_merge,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7333 normalize_merge_sources(apr_array_header_t **merge_sources_p,
7334 const char *source_path_or_url,
7335 const svn_client__pathrev_t *source_loc,
7336 const apr_array_header_t *ranges_to_merge,
7337 svn_ra_session_t *ra_session,
7338 svn_client_ctx_t *ctx,
7339 apr_pool_t *result_pool,
7340 apr_pool_t *scratch_pool)
7341 {
7342 const char *source_abspath_or_url;
7343 svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
7344 svn_rangelist_t *merge_range_ts;
7345 int i;
7346 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7347
7348 if(!svn_path_is_url(source_path_or_url))
7349 SVN_ERR(svn_dirent_get_absolute(&source_abspath_or_url, source_path_or_url,
7350 scratch_pool));
7351 else
7352 source_abspath_or_url = source_path_or_url;
7353
7354 /* Create a list to hold svn_merge_range_t's. */
7355 merge_range_ts = apr_array_make(scratch_pool, ranges_to_merge->nelts,
7356 sizeof(svn_merge_range_t *));
7357
7358 for (i = 0; i < ranges_to_merge->nelts; i++)
7359 {
7360 svn_opt_revision_range_t *range
7361 = APR_ARRAY_IDX(ranges_to_merge, i, svn_opt_revision_range_t *);
7362 svn_merge_range_t mrange;
7363
7364 svn_pool_clear(iterpool);
7365
7366 /* Resolve revisions to real numbers, validating as we go. */
7367 if ((range->start.kind == svn_opt_revision_unspecified)
7368 || (range->end.kind == svn_opt_revision_unspecified))
7369 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
7370 _("Not all required revisions are specified"));
7371
7372 SVN_ERR(svn_client__get_revision_number(&mrange.start, &youngest_rev,
7373 ctx->wc_ctx,
7374 source_abspath_or_url,
7375 ra_session, &range->start,
7376 iterpool));
7377 SVN_ERR(svn_client__get_revision_number(&mrange.end, &youngest_rev,
7378 ctx->wc_ctx,
7379 source_abspath_or_url,
7380 ra_session, &range->end,
7381 iterpool));
7382
7383 /* If this isn't a no-op range... */
7384 if (mrange.start != mrange.end)
7385 {
7386 /* ...then add it to the list. */
7387 mrange.inheritable = TRUE;
7388 APR_ARRAY_PUSH(merge_range_ts, svn_merge_range_t *)
7389 = svn_merge_range_dup(&mrange, scratch_pool);
7390 }
7391 }
7392
7393 SVN_ERR(normalize_merge_sources_internal(
7394 merge_sources_p, source_loc,
7395 merge_range_ts, ra_session, ctx, result_pool, scratch_pool));
7396
7397 svn_pool_destroy(iterpool);
7398 return SVN_NO_ERROR;
7399 }
7400
7401
7402 /*-----------------------------------------------------------------------*/
7403
7404 /*** Merge Workhorse Functions ***/
7405
7406 /* Helper for do_directory_merge() and do_file_merge() which filters out a
7407 path's own natural history from the mergeinfo describing a merge.
7408
7409 Given the natural history IMPLICIT_MERGEINFO of some wc merge target path,
7410 the repository-relative merge source path SOURCE_REL_PATH, and the
7411 requested merge range REQUESTED_RANGE from SOURCE_REL_PATH, remove any
7412 portion of REQUESTED_RANGE which is already described in
7413 IMPLICIT_MERGEINFO. Store the result in *FILTERED_RANGELIST.
7414
7415 This function only filters natural history for mergeinfo that will be
7416 *added* during a forward merge. Removing natural history from explicit
7417 mergeinfo is harmless. If REQUESTED_RANGE describes a reverse merge,
7418 then *FILTERED_RANGELIST is simply populated with one range described
7419 by REQUESTED_RANGE. *FILTERED_RANGELIST is never NULL.
7420
7421 Allocate *FILTERED_RANGELIST in POOL. */
7422 static svn_error_t *
filter_natural_history_from_mergeinfo(svn_rangelist_t ** filtered_rangelist,const char * source_rel_path,svn_mergeinfo_t implicit_mergeinfo,svn_merge_range_t * requested_range,apr_pool_t * pool)7423 filter_natural_history_from_mergeinfo(svn_rangelist_t **filtered_rangelist,
7424 const char *source_rel_path,
7425 svn_mergeinfo_t implicit_mergeinfo,
7426 svn_merge_range_t *requested_range,
7427 apr_pool_t *pool)
7428 {
7429 /* Make the REQUESTED_RANGE into a rangelist. */
7430 svn_rangelist_t *requested_rangelist =
7431 svn_rangelist__initialize(requested_range->start, requested_range->end,
7432 requested_range->inheritable, pool);
7433
7434 *filtered_rangelist = NULL;
7435
7436 /* For forward merges: If the IMPLICIT_MERGEINFO already describes ranges
7437 associated with SOURCE_REL_PATH then filter those ranges out. */
7438 if (implicit_mergeinfo
7439 && (requested_range->start < requested_range->end))
7440 {
7441 svn_rangelist_t *implied_rangelist =
7442 svn_hash_gets(implicit_mergeinfo, source_rel_path);
7443
7444 if (implied_rangelist)
7445 SVN_ERR(svn_rangelist_remove(filtered_rangelist,
7446 implied_rangelist,
7447 requested_rangelist,
7448 FALSE, pool));
7449 }
7450
7451 /* If no filtering was performed the filtered rangelist is
7452 simply the requested rangelist.*/
7453 if (! (*filtered_rangelist))
7454 *filtered_rangelist = requested_rangelist;
7455
7456 return SVN_NO_ERROR;
7457 }
7458
7459 /* Return a merge source representing the sub-range from START_REV to
7460 END_REV of SOURCE. SOURCE obeys the rules described in the
7461 'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file.
7462 The younger of START_REV and END_REV is inclusive while the older is
7463 exclusive.
7464
7465 Allocate the result structure in POOL but leave the URLs in it as shallow
7466 copies of the URLs in SOURCE.
7467 */
7468 static merge_source_t *
subrange_source(const merge_source_t * source,svn_revnum_t start_rev,svn_revnum_t end_rev,apr_pool_t * pool)7469 subrange_source(const merge_source_t *source,
7470 svn_revnum_t start_rev,
7471 svn_revnum_t end_rev,
7472 apr_pool_t *pool)
7473 {
7474 svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
7475 svn_boolean_t same_urls = (strcmp(source->loc1->url, source->loc2->url) == 0);
7476 svn_client__pathrev_t loc1 = *source->loc1;
7477 svn_client__pathrev_t loc2 = *source->loc2;
7478
7479 /* For this function we require that the input source is 'ancestral'. */
7480 SVN_ERR_ASSERT_NO_RETURN(source->ancestral);
7481 SVN_ERR_ASSERT_NO_RETURN(start_rev != end_rev);
7482
7483 loc1.rev = start_rev;
7484 loc2.rev = end_rev;
7485 if (! same_urls)
7486 {
7487 if (is_rollback && (end_rev != source->loc2->rev))
7488 {
7489 loc2.url = source->loc1->url;
7490 }
7491 if ((! is_rollback) && (start_rev != source->loc1->rev))
7492 {
7493 loc1.url = source->loc2->url;
7494 }
7495 }
7496 return merge_source_create(&loc1, &loc2, source->ancestral, pool);
7497 }
7498
7499 /* The single-file, simplified version of do_directory_merge(), which see for
7500 parameter descriptions.
7501
7502 Additional parameters:
7503
7504 If SOURCES_RELATED is set, the "left" and "right" sides of SOURCE are
7505 historically related (ancestors, uncles, second
7506 cousins thrice removed, etc...). (This is used to simulate the
7507 history checks that the repository logic does in the directory case.)
7508
7509 If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
7510 is not NULL, then don't record the new mergeinfo on the TARGET_ABSPATH,
7511 but instead record it in RESULT_CATALOG, where the key is TARGET_ABSPATH
7512 and the value is the new mergeinfo for that path. Allocate additions
7513 to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
7514
7515 CONFLICTED_RANGE is as documented for do_directory_merge().
7516
7517 Note: MERGE_B->RA_SESSION1 must be associated with SOURCE->loc1->url and
7518 MERGE_B->RA_SESSION2 with SOURCE->loc2->url.
7519 */
7520 static svn_error_t *
do_file_merge(svn_mergeinfo_catalog_t result_catalog,single_range_conflict_report_t ** conflict_report,const merge_source_t * source,const char * target_abspath,const svn_diff_tree_processor_t * processor,svn_boolean_t sources_related,svn_boolean_t squelch_mergeinfo_notifications,merge_cmd_baton_t * merge_b,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7521 do_file_merge(svn_mergeinfo_catalog_t result_catalog,
7522 single_range_conflict_report_t **conflict_report,
7523 const merge_source_t *source,
7524 const char *target_abspath,
7525 const svn_diff_tree_processor_t *processor,
7526 svn_boolean_t sources_related,
7527 svn_boolean_t squelch_mergeinfo_notifications,
7528 merge_cmd_baton_t *merge_b,
7529 apr_pool_t *result_pool,
7530 apr_pool_t *scratch_pool)
7531 {
7532 svn_rangelist_t *remaining_ranges;
7533 svn_client_ctx_t *ctx = merge_b->ctx;
7534 svn_merge_range_t range;
7535 svn_mergeinfo_t target_mergeinfo;
7536 svn_boolean_t inherited = FALSE;
7537 svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
7538 const svn_client__pathrev_t *primary_src
7539 = is_rollback ? source->loc1 : source->loc2;
7540 svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
7541 svn_client__merge_path_t *merge_target = NULL;
7542 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7543
7544 SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
7545
7546 *conflict_report = NULL;
7547
7548 /* Note that this is a single-file merge. */
7549 range.start = source->loc1->rev;
7550 range.end = source->loc2->rev;
7551 range.inheritable = TRUE;
7552
7553 merge_target = svn_client__merge_path_create(target_abspath, scratch_pool);
7554
7555 if (honor_mergeinfo)
7556 {
7557 svn_error_t *err;
7558
7559 /* Fetch mergeinfo. */
7560 err = get_full_mergeinfo(&target_mergeinfo,
7561 &(merge_target->implicit_mergeinfo),
7562 &inherited, svn_mergeinfo_inherited,
7563 merge_b->ra_session1, target_abspath,
7564 MAX(source->loc1->rev, source->loc2->rev),
7565 MIN(source->loc1->rev, source->loc2->rev),
7566 ctx, scratch_pool, iterpool);
7567
7568 if (err)
7569 {
7570 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
7571 {
7572 err = svn_error_createf(
7573 SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
7574 _("Invalid mergeinfo detected on merge target '%s', "
7575 "merge tracking not possible"),
7576 svn_dirent_local_style(target_abspath, scratch_pool));
7577 }
7578 return svn_error_trace(err);
7579 }
7580
7581 /* Calculate remaining merges unless this is a record only merge.
7582 In that case the remaining range is the whole range described
7583 by SOURCE->rev1:rev2. */
7584 if (!merge_b->record_only)
7585 {
7586 /* ### Bug? calculate_remaining_ranges() needs 'source' to adhere
7587 * to the requirements of 'MERGEINFO MERGE SOURCE NORMALIZATION'
7588 * here, but it doesn't appear to be guaranteed so. */
7589 SVN_ERR(calculate_remaining_ranges(NULL, merge_target,
7590 source,
7591 target_mergeinfo,
7592 merge_b->implicit_src_gap, FALSE,
7593 merge_b->ra_session1,
7594 ctx, scratch_pool,
7595 iterpool));
7596 remaining_ranges = merge_target->remaining_ranges;
7597
7598 /* We are honoring mergeinfo and this is not a simple record only
7599 merge which blindly records mergeinfo describing the merge of
7600 SOURCE->LOC1->URL@SOURCE->LOC1->REV through
7601 SOURCE->LOC2->URL@SOURCE->LOC2->REV. This means that the oldest
7602 and youngest revisions merged (as determined above by
7603 calculate_remaining_ranges) might differ from those described
7604 in SOURCE. To keep the '--- Merging *' notifications consistent
7605 with the '--- Recording mergeinfo *' notifications, we adjust
7606 RANGE to account for such changes. */
7607 if (remaining_ranges->nelts)
7608 {
7609 svn_merge_range_t *adj_start_range =
7610 APR_ARRAY_IDX(remaining_ranges, 0, svn_merge_range_t *);
7611 svn_merge_range_t *adj_end_range =
7612 APR_ARRAY_IDX(remaining_ranges, remaining_ranges->nelts - 1,
7613 svn_merge_range_t *);
7614 range.start = adj_start_range->start;
7615 range.end = adj_end_range->end;
7616 }
7617 }
7618 }
7619
7620 /* The simple cases where our remaining range is SOURCE->rev1:rev2. */
7621 if (!honor_mergeinfo || merge_b->record_only)
7622 {
7623 remaining_ranges = apr_array_make(scratch_pool, 1, sizeof(&range));
7624 APR_ARRAY_PUSH(remaining_ranges, svn_merge_range_t *) = ⦥
7625 }
7626
7627 if (!merge_b->record_only)
7628 {
7629 svn_rangelist_t *ranges_to_merge = apr_array_copy(scratch_pool,
7630 remaining_ranges);
7631 const char *target_relpath = ""; /* relative to root of merge */
7632
7633 if (source->ancestral)
7634 {
7635 apr_array_header_t *child_with_mergeinfo;
7636 svn_client__merge_path_t *target_info;
7637
7638 /* If we have ancestrally related sources and more than one
7639 range to merge, eliminate no-op ranges before going through
7640 the effort of downloading the many copies of the file
7641 required to do these merges (two copies per range). */
7642 if (remaining_ranges->nelts > 1)
7643 {
7644 const char *old_sess_url;
7645 svn_error_t *err;
7646
7647 SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url,
7648 merge_b->ra_session1,
7649 primary_src->url,
7650 iterpool));
7651 err = remove_noop_merge_ranges(&ranges_to_merge,
7652 merge_b->ra_session1,
7653 remaining_ranges, scratch_pool);
7654 SVN_ERR(svn_error_compose_create(
7655 err, svn_ra_reparent(merge_b->ra_session1,
7656 old_sess_url, iterpool)));
7657 }
7658
7659 /* To support notify_merge_begin() initialize our
7660 CHILD_WITH_MERGEINFO. See the comment
7661 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */
7662
7663 child_with_mergeinfo = apr_array_make(scratch_pool, 1,
7664 sizeof(svn_client__merge_path_t *));
7665
7666 /* ### Create a fake copy of merge_target as we don't keep
7667 remaining_ranges in sync (yet). */
7668 target_info = svn_client__merge_path_create(merge_target->abspath,
7669 scratch_pool);
7670 target_info->remaining_ranges = ranges_to_merge;
7671
7672 APR_ARRAY_PUSH(child_with_mergeinfo, svn_client__merge_path_t *)
7673 = target_info;
7674
7675 /* And store in baton to allow using it from notify_merge_begin() */
7676 merge_b->children_with_mergeinfo = child_with_mergeinfo;
7677 }
7678
7679 while (ranges_to_merge->nelts > 0)
7680 {
7681 svn_merge_range_t *r = APR_ARRAY_IDX(ranges_to_merge, 0,
7682 svn_merge_range_t *);
7683 const merge_source_t *real_source;
7684 const char *left_file, *right_file;
7685 apr_hash_t *left_props, *right_props;
7686 const svn_diff_source_t *left_source;
7687 const svn_diff_source_t *right_source;
7688
7689 svn_pool_clear(iterpool);
7690
7691 /* Ensure any subsequent drives gets their own notification. */
7692 merge_b->notify_begin.last_abspath = NULL;
7693
7694 /* While we currently don't allow it, in theory we could be
7695 fetching two fulltexts from two different repositories here. */
7696 if (source->ancestral)
7697 real_source = subrange_source(source, r->start, r->end, iterpool);
7698 else
7699 real_source = source;
7700 SVN_ERR(single_file_merge_get_file(&left_file, &left_props,
7701 merge_b->ra_session1,
7702 real_source->loc1,
7703 target_abspath,
7704 iterpool, iterpool));
7705 SVN_ERR(single_file_merge_get_file(&right_file, &right_props,
7706 merge_b->ra_session2,
7707 real_source->loc2,
7708 target_abspath,
7709 iterpool, iterpool));
7710 /* Calculate sources for the diff processor */
7711 left_source = svn_diff__source_create(r->start, iterpool);
7712 right_source = svn_diff__source_create(r->end, iterpool);
7713
7714
7715 /* If the sources are related or we're ignoring ancestry in diffs,
7716 do a text-n-props merge; otherwise, do a delete-n-add merge. */
7717 if (! (merge_b->diff_ignore_ancestry || sources_related))
7718 {
7719 void *dir_baton = open_dir_for_replace_single_file(iterpool);
7720 void *file_baton;
7721 svn_boolean_t skip;
7722
7723 /* Delete... */
7724 file_baton = NULL;
7725 skip = FALSE;
7726 SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7727 left_source,
7728 NULL /* right_source */,
7729 NULL /* copyfrom_source */,
7730 dir_baton,
7731 processor,
7732 iterpool, iterpool));
7733 if (! skip)
7734 SVN_ERR(processor->file_deleted(target_relpath,
7735 left_source,
7736 left_file,
7737 left_props,
7738 file_baton,
7739 processor,
7740 iterpool));
7741
7742 /* ...plus add... */
7743 file_baton = NULL;
7744 skip = FALSE;
7745 SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7746 NULL /* left_source */,
7747 right_source,
7748 NULL /* copyfrom_source */,
7749 dir_baton,
7750 processor,
7751 iterpool, iterpool));
7752 if (! skip)
7753 SVN_ERR(processor->file_added(target_relpath,
7754 NULL /* copyfrom_source */,
7755 right_source,
7756 NULL /* copyfrom_file */,
7757 right_file,
7758 NULL /* copyfrom_props */,
7759 right_props,
7760 file_baton,
7761 processor,
7762 iterpool));
7763 /* ... equals replace. */
7764 }
7765 else
7766 {
7767 void *file_baton = NULL;
7768 svn_boolean_t skip = FALSE;
7769 apr_array_header_t *propchanges;
7770
7771
7772 /* Deduce property diffs. */
7773 SVN_ERR(svn_prop_diffs(&propchanges, right_props, left_props,
7774 iterpool));
7775
7776 SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7777 left_source,
7778 right_source,
7779 NULL /* copyfrom_source */,
7780 NULL /* dir_baton */,
7781 processor,
7782 iterpool, iterpool));
7783 if (! skip)
7784 SVN_ERR(processor->file_changed(target_relpath,
7785 left_source,
7786 right_source,
7787 left_file,
7788 right_file,
7789 left_props,
7790 right_props,
7791 TRUE /* file changed */,
7792 propchanges,
7793 file_baton,
7794 processor,
7795 iterpool));
7796 }
7797
7798 if (is_path_conflicted_by_merge(merge_b))
7799 {
7800 merge_source_t *remaining_range = NULL;
7801
7802 if (real_source->loc2->rev != source->loc2->rev)
7803 remaining_range = subrange_source(source,
7804 real_source->loc2->rev,
7805 source->loc2->rev,
7806 scratch_pool);
7807 *conflict_report = single_range_conflict_report_create(
7808 real_source, remaining_range, result_pool);
7809
7810 /* Only record partial mergeinfo if only a partial merge was
7811 performed before a conflict was encountered. */
7812 range.end = r->end;
7813 break;
7814 }
7815
7816 /* Now delete the just merged range from the hash
7817 (This list is used from notify_merge_begin)
7818
7819 Directory merges use remove_first_range_from_remaining_ranges() */
7820 SVN_ERR(svn_sort__array_delete2(ranges_to_merge, 0, 1));
7821 }
7822 merge_b->notify_begin.last_abspath = NULL;
7823 } /* !merge_b->record_only */
7824
7825 /* Record updated WC mergeinfo to account for our new merges, minus
7826 any unresolved conflicts and skips. We use the original
7827 REMAINING_RANGES here because we want to record all the requested
7828 merge ranges, include the noop ones. */
7829 if (RECORD_MERGEINFO(merge_b) && remaining_ranges->nelts)
7830 {
7831 const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
7832 scratch_pool);
7833 svn_rangelist_t *filtered_rangelist;
7834
7835 /* Filter any ranges from TARGET_WCPATH's own history, there is no
7836 need to record this explicitly in mergeinfo, it is already part
7837 of TARGET_WCPATH's natural history (implicit mergeinfo). */
7838 SVN_ERR(filter_natural_history_from_mergeinfo(
7839 &filtered_rangelist,
7840 mergeinfo_path,
7841 merge_target->implicit_mergeinfo,
7842 &range,
7843 iterpool));
7844
7845 /* Only record mergeinfo if there is something other than
7846 self-referential mergeinfo, but don't record mergeinfo if
7847 TARGET_WCPATH was skipped. */
7848 if (filtered_rangelist->nelts
7849 && (apr_hash_count(merge_b->skipped_abspaths) == 0))
7850 {
7851 apr_hash_t *merges = apr_hash_make(iterpool);
7852
7853 /* If merge target has inherited mergeinfo set it before
7854 recording the first merge range. */
7855 if (inherited)
7856 SVN_ERR(svn_client__record_wc_mergeinfo(target_abspath,
7857 target_mergeinfo,
7858 FALSE, ctx,
7859 iterpool));
7860
7861 svn_hash_sets(merges, target_abspath, filtered_rangelist);
7862
7863 if (!squelch_mergeinfo_notifications)
7864 {
7865 /* Notify that we are recording mergeinfo describing a merge. */
7866 svn_merge_range_t n_range;
7867
7868 SVN_ERR(svn_mergeinfo__get_range_endpoints(
7869 &n_range.end, &n_range.start, merges, iterpool));
7870 n_range.inheritable = TRUE;
7871 notify_mergeinfo_recording(target_abspath, &n_range,
7872 merge_b->ctx, iterpool);
7873 }
7874
7875 SVN_ERR(update_wc_mergeinfo(result_catalog, target_abspath,
7876 mergeinfo_path, merges, is_rollback,
7877 ctx, iterpool));
7878 }
7879 }
7880
7881 merge_b->children_with_mergeinfo = NULL;
7882
7883 svn_pool_destroy(iterpool);
7884
7885 return SVN_NO_ERROR;
7886 }
7887
7888 /* Helper for do_directory_merge() to handle the case where a merge editor
7889 drive adds explicit mergeinfo to a path which didn't have any explicit
7890 mergeinfo previously.
7891
7892 MERGE_B is cascaded from the argument of the same
7893 name in do_directory_merge(). Should be called only after
7894 do_directory_merge() has called populate_remaining_ranges() and populated
7895 the remaining_ranges field of each child in
7896 CHILDREN_WITH_MERGEINFO (i.e. the remaining_ranges fields can be
7897 empty but never NULL).
7898
7899 If MERGE_B->DRY_RUN is true do nothing, if it is false then
7900 for each path (if any) in MERGE_B->PATHS_WITH_NEW_MERGEINFO merge that
7901 path's inherited mergeinfo (if any) with its working explicit mergeinfo
7902 and set that as the path's new explicit mergeinfo. Then add an
7903 svn_client__merge_path_t * element representing the path to
7904 CHILDREN_WITH_MERGEINFO if it isn't already present. All fields
7905 in any elements added to CHILDREN_WITH_MERGEINFO are initialized
7906 to FALSE/NULL with the exception of 'path' and 'remaining_ranges'. The
7907 latter is set to a rangelist equal to the remaining_ranges of the path's
7908 nearest path-wise ancestor in CHILDREN_WITH_MERGEINFO.
7909
7910 Any elements added to CHILDREN_WITH_MERGEINFO are allocated
7911 in POOL. */
7912 static svn_error_t *
process_children_with_new_mergeinfo(merge_cmd_baton_t * merge_b,apr_array_header_t * children_with_mergeinfo,apr_pool_t * pool)7913 process_children_with_new_mergeinfo(merge_cmd_baton_t *merge_b,
7914 apr_array_header_t *children_with_mergeinfo,
7915 apr_pool_t *pool)
7916 {
7917 apr_pool_t *iterpool;
7918 apr_array_header_t *a;
7919 int i;
7920
7921 if (!merge_b->paths_with_new_mergeinfo || merge_b->dry_run)
7922 return SVN_NO_ERROR;
7923
7924 /* Iterate over each path with explicit mergeinfo added by the merge. */
7925 /* Iterate over the paths in a parent-to-child order so that inherited
7926 * mergeinfo is propagated consistently from each parent path to its
7927 * children. (Issue #4862) */
7928 a = svn_sort__hash(merge_b->paths_with_new_mergeinfo,
7929 svn_sort_compare_items_as_paths, pool);
7930 iterpool = svn_pool_create(pool);
7931 for (i = 0; i < a->nelts; i++)
7932 {
7933 svn_sort__item_t *item = &APR_ARRAY_IDX(a, i, svn_sort__item_t);
7934 const char *abspath_with_new_mergeinfo = item->key;
7935 svn_mergeinfo_t path_inherited_mergeinfo;
7936 svn_mergeinfo_t path_explicit_mergeinfo;
7937 svn_client__merge_path_t *new_child;
7938
7939 svn_pool_clear(iterpool);
7940
7941 /* Note: We could skip recording inherited mergeinfo here if this path
7942 was added (with preexisting mergeinfo) by the merge. That's actually
7943 more correct, since the inherited mergeinfo likely describes
7944 non-existent or unrelated merge history, but it's not quite so simple
7945 as that, see https://issues.apache.org/jira/browse/SVN-4309
7946 */
7947
7948 /* Get the path's new explicit mergeinfo... */
7949 SVN_ERR(svn_client__get_wc_mergeinfo(&path_explicit_mergeinfo, NULL,
7950 svn_mergeinfo_explicit,
7951 abspath_with_new_mergeinfo,
7952 NULL, NULL, FALSE,
7953 merge_b->ctx,
7954 iterpool, iterpool));
7955 /* ...there *should* always be explicit mergeinfo at this point
7956 but you can't be too careful. */
7957 if (path_explicit_mergeinfo)
7958 {
7959 /* Get the mergeinfo the path would have inherited before
7960 the merge. */
7961 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(
7962 &path_inherited_mergeinfo,
7963 NULL, NULL,
7964 FALSE,
7965 svn_mergeinfo_nearest_ancestor, /* We only want inherited MI */
7966 merge_b->ra_session2,
7967 abspath_with_new_mergeinfo,
7968 merge_b->ctx,
7969 iterpool));
7970
7971 /* If the path inherited any mergeinfo then merge that with the
7972 explicit mergeinfo and record the result as the path's new
7973 explicit mergeinfo. */
7974 if (path_inherited_mergeinfo)
7975 {
7976 SVN_ERR(svn_mergeinfo_merge2(path_explicit_mergeinfo,
7977 path_inherited_mergeinfo,
7978 iterpool, iterpool));
7979 SVN_ERR(svn_client__record_wc_mergeinfo(
7980 abspath_with_new_mergeinfo,
7981 path_explicit_mergeinfo,
7982 FALSE, merge_b->ctx, iterpool));
7983 }
7984
7985 /* If the path is not in CHILDREN_WITH_MERGEINFO then add it. */
7986 new_child =
7987 get_child_with_mergeinfo(children_with_mergeinfo,
7988 abspath_with_new_mergeinfo);
7989 if (!new_child)
7990 {
7991 const svn_client__merge_path_t *parent
7992 = find_nearest_ancestor(children_with_mergeinfo,
7993 FALSE, abspath_with_new_mergeinfo);
7994 new_child
7995 = svn_client__merge_path_create(abspath_with_new_mergeinfo,
7996 pool);
7997
7998 /* If path_with_new_mergeinfo is the merge target itself
7999 then it should already be in
8000 CHILDREN_WITH_MERGEINFO per the criteria of
8001 get_mergeinfo_paths() and we shouldn't be in this block.
8002 If path_with_new_mergeinfo is a subtree then it must have
8003 a parent in CHILDREN_WITH_MERGEINFO if only
8004 the merge target itself...so if we don't find a parent
8005 the caller has done something quite wrong. */
8006 SVN_ERR_ASSERT(parent);
8007 SVN_ERR_ASSERT(parent->remaining_ranges);
8008
8009 /* Set the path's remaining_ranges equal to its parent's. */
8010 new_child->remaining_ranges = svn_rangelist_dup(
8011 parent->remaining_ranges, pool);
8012 SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
8013 new_child, pool));
8014 }
8015 }
8016 }
8017 svn_pool_destroy(iterpool);
8018
8019 return SVN_NO_ERROR;
8020 }
8021
8022 /* Return true if any path in SUBTREES is equal to, or is a subtree of,
8023 LOCAL_ABSPATH. Return false otherwise. The keys of SUBTREES are
8024 (const char *) absolute paths and its values are irrelevant.
8025 If SUBTREES is NULL return false. */
8026 static svn_boolean_t
path_is_subtree(const char * local_abspath,apr_hash_t * subtrees,apr_pool_t * pool)8027 path_is_subtree(const char *local_abspath,
8028 apr_hash_t *subtrees,
8029 apr_pool_t *pool)
8030 {
8031 if (subtrees)
8032 {
8033 apr_hash_index_t *hi;
8034
8035 for (hi = apr_hash_first(pool, subtrees);
8036 hi; hi = apr_hash_next(hi))
8037 {
8038 const char *path_touched_by_merge = apr_hash_this_key(hi);
8039 if (svn_dirent_is_ancestor(local_abspath, path_touched_by_merge))
8040 return TRUE;
8041 }
8042 }
8043 return FALSE;
8044 }
8045
8046 /* Return true if any merged, skipped, added or tree-conflicted path
8047 recorded in MERGE_B is equal to, or is a subtree of LOCAL_ABSPATH. Return
8048 false otherwise.
8049
8050 ### Why not text- or prop-conflicted paths? Are such paths guaranteed
8051 to be recorded as 'merged' or 'skipped' or 'added', perhaps?
8052 */
8053 static svn_boolean_t
subtree_touched_by_merge(const char * local_abspath,merge_cmd_baton_t * merge_b,apr_pool_t * pool)8054 subtree_touched_by_merge(const char *local_abspath,
8055 merge_cmd_baton_t *merge_b,
8056 apr_pool_t *pool)
8057 {
8058 return (path_is_subtree(local_abspath, merge_b->merged_abspaths, pool)
8059 || path_is_subtree(local_abspath, merge_b->skipped_abspaths, pool)
8060 || path_is_subtree(local_abspath, merge_b->added_abspaths, pool)
8061 || path_is_subtree(local_abspath, merge_b->tree_conflicted_abspaths,
8062 pool));
8063 }
8064
8065 /* Helper for do_directory_merge() when performing mergeinfo unaware merges.
8066
8067 Merge the SOURCE diff into TARGET_DIR_WCPATH.
8068
8069 SOURCE, DEPTH, NOTIFY_B, and MERGE_B
8070 are all cascaded from do_directory_merge's arguments of the same names.
8071
8072 CONFLICT_REPORT is as documented for do_directory_merge().
8073
8074 NOTE: This is a very thin wrapper around drive_merge_report_editor() and
8075 exists only to populate CHILDREN_WITH_MERGEINFO with the single element
8076 expected during mergeinfo unaware merges.
8077 */
8078 static svn_error_t *
do_mergeinfo_unaware_dir_merge(single_range_conflict_report_t ** conflict_report,const merge_source_t * source,const char * target_dir_wcpath,apr_array_header_t * children_with_mergeinfo,const svn_diff_tree_processor_t * processor,svn_depth_t depth,merge_cmd_baton_t * merge_b,apr_pool_t * result_pool,apr_pool_t * scratch_pool)8079 do_mergeinfo_unaware_dir_merge(single_range_conflict_report_t **conflict_report,
8080 const merge_source_t *source,
8081 const char *target_dir_wcpath,
8082 apr_array_header_t *children_with_mergeinfo,
8083 const svn_diff_tree_processor_t *processor,
8084 svn_depth_t depth,
8085 merge_cmd_baton_t *merge_b,
8086 apr_pool_t *result_pool,
8087 apr_pool_t *scratch_pool)
8088 {
8089 /* Initialize CHILDREN_WITH_MERGEINFO and populate it with
8090 one element describing the merge of SOURCE->rev1:rev2 to
8091 TARGET_DIR_WCPATH. */
8092 svn_client__merge_path_t *item
8093 = svn_client__merge_path_create(target_dir_wcpath, scratch_pool);
8094
8095 *conflict_report = NULL;
8096 item->remaining_ranges = svn_rangelist__initialize(source->loc1->rev,
8097 source->loc2->rev,
8098 TRUE, scratch_pool);
8099 APR_ARRAY_PUSH(children_with_mergeinfo,
8100 svn_client__merge_path_t *) = item;
8101 SVN_ERR(drive_merge_report_editor(target_dir_wcpath,
8102 source,
8103 NULL, processor, depth,
8104 merge_b, scratch_pool));
8105 if (is_path_conflicted_by_merge(merge_b))
8106 {
8107 *conflict_report = single_range_conflict_report_create(
8108 source, NULL, result_pool);
8109 }
8110 return SVN_NO_ERROR;
8111 }
8112
8113 /* A svn_log_entry_receiver_t baton for log_find_operative_subtree_revs(). */
8114 typedef struct log_find_operative_subtree_baton_t
8115 {
8116 /* Mapping of const char * absolute working copy paths to those
8117 path's const char * repos absolute paths. */
8118 apr_hash_t *operative_children;
8119
8120 /* As per the arguments of the same name to
8121 get_operative_immediate_children(). */
8122 const char *merge_source_fspath;
8123 const char *merge_target_abspath;
8124 svn_depth_t depth;
8125 svn_wc_context_t *wc_ctx;
8126
8127 /* A pool to allocate additions to the hashes in. */
8128 apr_pool_t *result_pool;
8129 } log_find_operative_subtree_baton_t;
8130
8131 /* A svn_log_entry_receiver_t callback for
8132 get_inoperative_immediate_children(). */
8133 static svn_error_t *
log_find_operative_subtree_revs(void * baton,svn_log_entry_t * log_entry,apr_pool_t * pool)8134 log_find_operative_subtree_revs(void *baton,
8135 svn_log_entry_t *log_entry,
8136 apr_pool_t *pool)
8137 {
8138 log_find_operative_subtree_baton_t *log_baton = baton;
8139 apr_hash_index_t *hi;
8140 apr_pool_t *iterpool;
8141
8142 /* It's possible that authz restrictions on the merge source prevent us
8143 from knowing about any of the changes for LOG_ENTRY->REVISION. */
8144 if (!log_entry->changed_paths2)
8145 return SVN_NO_ERROR;
8146
8147 iterpool = svn_pool_create(pool);
8148
8149 for (hi = apr_hash_first(pool, log_entry->changed_paths2);
8150 hi;
8151 hi = apr_hash_next(hi))
8152 {
8153 const char *path = apr_hash_this_key(hi);
8154 svn_log_changed_path2_t *change = apr_hash_this_val(hi);
8155
8156 {
8157 const char *child;
8158 const char *potential_child;
8159 const char *rel_path =
8160 svn_fspath__skip_ancestor(log_baton->merge_source_fspath, path);
8161
8162 /* Some affected paths might be the root of the merge source or
8163 entirely outside our subtree of interest. In either case they
8164 are not operative *immediate* children. */
8165 if (rel_path == NULL
8166 || rel_path[0] == '\0')
8167 continue;
8168
8169 svn_pool_clear(iterpool);
8170
8171 child = svn_relpath_dirname(rel_path, iterpool);
8172 if (child[0] == '\0')
8173 {
8174 /* The svn_log_changed_path2_t.node_kind members in
8175 LOG_ENTRY->CHANGED_PATHS2 may be set to
8176 svn_node_unknown, see svn_log_changed_path2_t and
8177 svn_fs_paths_changed2. In that case we check the
8178 type of the corresponding subtree in the merge
8179 target. */
8180 svn_node_kind_t node_kind;
8181
8182 if (change->node_kind == svn_node_unknown)
8183 {
8184 const char *wc_child_abspath =
8185 svn_dirent_join(log_baton->merge_target_abspath,
8186 rel_path, iterpool);
8187
8188 SVN_ERR(svn_wc_read_kind2(&node_kind, log_baton->wc_ctx,
8189 wc_child_abspath, FALSE, FALSE,
8190 iterpool));
8191 }
8192 else
8193 {
8194 node_kind = change->node_kind;
8195 }
8196
8197 /* We only care about immediate directory children if
8198 DEPTH is svn_depth_files. */
8199 if (log_baton->depth == svn_depth_files
8200 && node_kind != svn_node_dir)
8201 continue;
8202
8203 /* If depth is svn_depth_immediates, then we only care
8204 about changes to proper subtrees of PATH. If the change
8205 is to PATH itself then PATH is within the operational
8206 depth of the merge. */
8207 if (log_baton->depth == svn_depth_immediates)
8208 continue;
8209
8210 child = rel_path;
8211 }
8212
8213 potential_child = svn_dirent_join(log_baton->merge_target_abspath,
8214 child, iterpool);
8215
8216 if (change->action == 'A'
8217 || !svn_hash_gets(log_baton->operative_children,
8218 potential_child))
8219 {
8220 svn_hash_sets(log_baton->operative_children,
8221 apr_pstrdup(log_baton->result_pool,
8222 potential_child),
8223 apr_pstrdup(log_baton->result_pool, path));
8224 }
8225 }
8226 }
8227 svn_pool_destroy(iterpool);
8228 return SVN_NO_ERROR;
8229 }
8230
8231 /* Find immediate subtrees of MERGE_TARGET_ABSPATH which would have
8232 additional differences applied if record_mergeinfo_for_dir_merge() were
8233 recording mergeinfo describing a merge at svn_depth_infinity, rather
8234 than at DEPTH (which is assumed to be shallow; if
8235 DEPTH == svn_depth_infinity then this function does nothing beyond
8236 setting *OPERATIVE_CHILDREN to an empty hash).
8237
8238 MERGE_SOURCE_FSPATH is the absolute repository path of the merge
8239 source. OLDEST_REV and YOUNGEST_REV are the revisions merged from
8240 MERGE_SOURCE_FSPATH to MERGE_TARGET_ABSPATH.
8241
8242 RA_SESSION points to MERGE_SOURCE_FSPATH.
8243
8244 Set *OPERATIVE_CHILDREN to a hash (mapping const char * absolute
8245 working copy paths to those path's const char * repos absolute paths)
8246 containing all the immediate subtrees of MERGE_TARGET_ABSPATH which would
8247 have a different diff applied if MERGE_SOURCE_FSPATH
8248 -r(OLDEST_REV - 1):YOUNGEST_REV were merged to MERGE_TARGET_ABSPATH at
8249 svn_depth_infinity rather than DEPTH.
8250
8251 RESULT_POOL is used to allocate the contents of *OPERATIVE_CHILDREN.
8252 SCRATCH_POOL is used for temporary allocations. */
8253 static svn_error_t *
get_operative_immediate_children(apr_hash_t ** operative_children,const char * merge_source_fspath,svn_revnum_t oldest_rev,svn_revnum_t youngest_rev,const char * merge_target_abspath,svn_depth_t depth,svn_wc_context_t * wc_ctx,svn_ra_session_t * ra_session,apr_pool_t * result_pool,apr_pool_t * scratch_pool)8254 get_operative_immediate_children(apr_hash_t **operative_children,
8255 const char *merge_source_fspath,
8256 svn_revnum_t oldest_rev,
8257 svn_revnum_t youngest_rev,
8258 const char *merge_target_abspath,
8259 svn_depth_t depth,
8260 svn_wc_context_t *wc_ctx,
8261 svn_ra_session_t *ra_session,
8262 apr_pool_t *result_pool,
8263 apr_pool_t *scratch_pool)
8264 {
8265 log_find_operative_subtree_baton_t log_baton;
8266
8267 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
8268 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
8269 SVN_ERR_ASSERT(oldest_rev <= youngest_rev);
8270
8271 *operative_children = apr_hash_make(result_pool);
8272
8273 if (depth == svn_depth_infinity)
8274 return SVN_NO_ERROR;
8275
8276 /* Now remove any paths from *OPERATIVE_CHILDREN that are inoperative when
8277 merging MERGE_SOURCE_REPOS_PATH -r(OLDEST_REV - 1):YOUNGEST_REV to
8278 MERGE_TARGET_ABSPATH at --depth infinity. */
8279 log_baton.operative_children = *operative_children;
8280 log_baton.merge_source_fspath = merge_source_fspath;
8281 log_baton.merge_target_abspath = merge_target_abspath;
8282 log_baton.depth = depth;
8283 log_baton.wc_ctx = wc_ctx;
8284 log_baton.result_pool = result_pool;
8285
8286 SVN_ERR(get_log(ra_session, "", youngest_rev, oldest_rev,
8287 TRUE, /* discover_changed_paths */
8288 log_find_operative_subtree_revs,
8289 &log_baton, scratch_pool));
8290
8291 return SVN_NO_ERROR;
8292 }
8293
8294 /* Helper for record_mergeinfo_for_dir_merge(): Identify which elements of
8295 CHILDREN_WITH_MERGEINFO need new mergeinfo set to accurately
8296 describe a merge, what inheritance type such new mergeinfo should have,
8297 and what subtrees can be ignored altogether.
8298
8299 For each svn_client__merge_path_t CHILD in CHILDREN_WITH_MERGEINFO,
8300 set CHILD->RECORD_MERGEINFO and CHILD->RECORD_NONINHERITABLE to true
8301 if the subtree needs mergeinfo to describe the merge and if that
8302 mergeinfo should be non-inheritable respectively.
8303
8304 If OPERATIVE_MERGE is true, then the merge being described is operative
8305 as per subtree_touched_by_merge(). OPERATIVE_MERGE is false otherwise.
8306
8307 MERGED_RANGE, MERGEINFO_FSPATH, DEPTH, NOTIFY_B, and MERGE_B are all
8308 cascaded from record_mergeinfo_for_dir_merge's arguments of the same
8309 names.
8310
8311 SCRATCH_POOL is used for temporary allocations.
8312 */
8313 static svn_error_t *
flag_subtrees_needing_mergeinfo(svn_boolean_t operative_merge,const svn_merge_range_t * merged_range,apr_array_header_t * children_with_mergeinfo,const char * mergeinfo_fspath,svn_depth_t depth,merge_cmd_baton_t * merge_b,apr_pool_t * scratch_pool)8314 flag_subtrees_needing_mergeinfo(svn_boolean_t operative_merge,
8315 const svn_merge_range_t *merged_range,
8316 apr_array_header_t *children_with_mergeinfo,
8317 const char *mergeinfo_fspath,
8318 svn_depth_t depth,
8319 merge_cmd_baton_t *merge_b,
8320 apr_pool_t *scratch_pool)
8321 {
8322 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
8323 int i;
8324 apr_hash_t *operative_immediate_children = NULL;
8325
8326 assert(! merge_b->dry_run);
8327
8328 if (!merge_b->record_only
8329 && merged_range->start <= merged_range->end
8330 && (depth < svn_depth_infinity))
8331 SVN_ERR(get_operative_immediate_children(
8332 &operative_immediate_children,
8333 mergeinfo_fspath, merged_range->start + 1, merged_range->end,
8334 merge_b->target->abspath, depth, merge_b->ctx->wc_ctx,
8335 merge_b->ra_session1, scratch_pool, iterpool));
8336
8337 /* Issue #4056: Walk CHILDREN_WITH_MERGEINFO reverse depth-first
8338 order. This way each child knows if it has operative missing/switched
8339 children which necessitates non-inheritable mergeinfo. */
8340 for (i = children_with_mergeinfo->nelts - 1; i >= 0; i--)
8341 {
8342 svn_client__merge_path_t *child =
8343 APR_ARRAY_IDX(children_with_mergeinfo, i,
8344 svn_client__merge_path_t *);
8345
8346 /* Can't record mergeinfo on something that isn't here. */
8347 if (child->absent)
8348 continue;
8349
8350 /* Verify that remove_children_with_deleted_mergeinfo() did its job */
8351 assert((i == 0)
8352 ||! merge_b->paths_with_deleted_mergeinfo
8353 || !svn_hash_gets(merge_b->paths_with_deleted_mergeinfo,
8354 child->abspath));
8355
8356 /* Don't record mergeinfo on skipped paths. */
8357 if (svn_hash_gets(merge_b->skipped_abspaths, child->abspath))
8358 continue;
8359
8360 /* ### ptb: Yes, we could combine the following into a single
8361 ### conditional, but clarity would suffer (even more than
8362 ### it does now). */
8363 if (i == 0)
8364 {
8365 /* Always record mergeinfo on the merge target. */
8366 child->record_mergeinfo = TRUE;
8367 }
8368 else if (merge_b->record_only && !merge_b->reintegrate_merge)
8369 {
8370 /* Always record mergeinfo for --record-only merges. */
8371 child->record_mergeinfo = TRUE;
8372 }
8373 else if (child->immediate_child_dir
8374 && !child->pre_merge_mergeinfo
8375 && operative_immediate_children
8376 && svn_hash_gets(operative_immediate_children, child->abspath))
8377 {
8378 /* We must record mergeinfo on those issue #3642 children
8379 that are operative at a greater depth. */
8380 child->record_mergeinfo = TRUE;
8381 }
8382
8383 if (operative_merge
8384 && subtree_touched_by_merge(child->abspath, merge_b, iterpool))
8385 {
8386 svn_pool_clear(iterpool);
8387
8388 /* This subtree was affected by the merge. */
8389 child->record_mergeinfo = TRUE;
8390
8391 /* Were any CHILD's missing children skipped by the merge?
8392 If not, then CHILD's missing children don't need to be
8393 considered when recording mergeinfo describing the merge. */
8394 if (! merge_b->reintegrate_merge
8395 && child->missing_child
8396 && !path_is_subtree(child->abspath,
8397 merge_b->skipped_abspaths,
8398 iterpool))
8399 {
8400 child->missing_child = FALSE;
8401 }
8402
8403 /* If CHILD has an immediate switched child or children and
8404 none of these were touched by the merge, then we don't need
8405 need to do any special handling of those switched subtrees
8406 (e.g. record non-inheritable mergeinfo) when recording
8407 mergeinfo describing the merge. */
8408 if (child->switched_child)
8409 {
8410 int j;
8411 svn_boolean_t operative_switched_child = FALSE;
8412
8413 for (j = i + 1;
8414 j < children_with_mergeinfo->nelts;
8415 j++)
8416 {
8417 svn_client__merge_path_t *potential_child =
8418 APR_ARRAY_IDX(children_with_mergeinfo, j,
8419 svn_client__merge_path_t *);
8420 if (!svn_dirent_is_ancestor(child->abspath,
8421 potential_child->abspath))
8422 break;
8423
8424 /* POTENTIAL_CHILD is a subtree of CHILD, but is it
8425 an immediate child? */
8426 if (strcmp(child->abspath,
8427 svn_dirent_dirname(potential_child->abspath,
8428 iterpool)))
8429 continue;
8430
8431 if (potential_child->switched
8432 && potential_child->record_mergeinfo)
8433 {
8434 operative_switched_child = TRUE;
8435 break;
8436 }
8437 }
8438
8439 /* Can we treat CHILD as if it has no switched children? */
8440 if (! operative_switched_child)
8441 child->switched_child = FALSE;
8442 }
8443 }
8444
8445 if (child->record_mergeinfo)
8446 {
8447 /* We need to record mergeinfo, but should that mergeinfo be
8448 non-inheritable? */
8449 svn_node_kind_t path_kind;
8450 SVN_ERR(svn_wc_read_kind2(&path_kind, merge_b->ctx->wc_ctx,
8451 child->abspath, FALSE, FALSE, iterpool));
8452
8453 /* Only directories can have non-inheritable mergeinfo. */
8454 if (path_kind == svn_node_dir)
8455 {
8456 /* There are two general cases where non-inheritable mergeinfo
8457 is required:
8458
8459 1) There merge target has missing subtrees (due to authz
8460 restrictions, switched subtrees, or a shallow working
8461 copy).
8462
8463 2) The operational depth of the merge itself is shallow. */
8464
8465 /* We've already determined the first case. */
8466 child->record_noninheritable =
8467 child->missing_child || child->switched_child;
8468
8469 /* The second case requires a bit more work. */
8470 if (i == 0)
8471 {
8472 /* If CHILD is the root of the merge target and the
8473 operational depth is empty or files, then the mere
8474 existence of operative immediate children means we
8475 must record non-inheritable mergeinfo.
8476
8477 ### What about svn_depth_immediates? In that case
8478 ### the merge target needs only normal inheritable
8479 ### mergeinfo and the target's immediate children will
8480 ### get non-inheritable mergeinfo, assuming they
8481 ### need even that. */
8482 if (depth < svn_depth_immediates
8483 && operative_immediate_children
8484 && apr_hash_count(operative_immediate_children))
8485 child->record_noninheritable = TRUE;
8486 }
8487 else if (depth == svn_depth_immediates)
8488 {
8489 /* An immediate directory child of the merge target, which
8490 was affected by a --depth=immediates merge, needs
8491 non-inheritable mergeinfo. */
8492 if (svn_hash_gets(operative_immediate_children,
8493 child->abspath))
8494 child->record_noninheritable = TRUE;
8495 }
8496 }
8497 }
8498 else /* child->record_mergeinfo */
8499 {
8500 /* If CHILD is in CHILDREN_WITH_MERGEINFO simply
8501 because it had no explicit mergeinfo of its own at the
8502 start of the merge but is the child of of some path with
8503 non-inheritable mergeinfo, then the explicit mergeinfo it
8504 has *now* was set by get_mergeinfo_paths() -- see criteria
8505 3 in that function's doc string. So since CHILD->ABSPATH
8506 was not touched by the merge we can remove the
8507 mergeinfo. */
8508 if (child->child_of_noninheritable)
8509 SVN_ERR(svn_client__record_wc_mergeinfo(child->abspath,
8510 NULL, FALSE,
8511 merge_b->ctx,
8512 iterpool));
8513 }
8514 }
8515
8516 svn_pool_destroy(iterpool);
8517 return SVN_NO_ERROR;
8518 }
8519
8520 /* Helper for do_directory_merge().
8521
8522 If RESULT_CATALOG is NULL then record mergeinfo describing a merge of
8523 MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
8524 MERGEINFO_FSPATH to the merge target (and possibly its subtrees) described
8525 by CHILDREN_WITH_MERGEINFO -- see the global comment
8526 'THE CHILDREN_WITH_MERGEINFO ARRAY'. Obviously this should only
8527 be called if recording mergeinfo -- see doc string for RECORD_MERGEINFO().
8528
8529 If RESULT_CATALOG is not NULL, then don't record the new mergeinfo on the
8530 WC, but instead record it in RESULT_CATALOG, where the keys are absolute
8531 working copy paths and the values are the new mergeinfos for each.
8532 Allocate additions to RESULT_CATALOG in pool which RESULT_CATALOG was
8533 created in.
8534
8535 DEPTH, NOTIFY_B, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS are all
8536 cascaded from do_directory_merge's arguments of the same names.
8537
8538 SCRATCH_POOL is used for temporary allocations.
8539 */
8540 static svn_error_t *
record_mergeinfo_for_dir_merge(svn_mergeinfo_catalog_t result_catalog,const svn_merge_range_t * merged_range,const char * mergeinfo_fspath,apr_array_header_t * children_with_mergeinfo,svn_depth_t depth,svn_boolean_t squelch_mergeinfo_notifications,merge_cmd_baton_t * merge_b,apr_pool_t * scratch_pool)8541 record_mergeinfo_for_dir_merge(svn_mergeinfo_catalog_t result_catalog,
8542 const svn_merge_range_t *merged_range,
8543 const char *mergeinfo_fspath,
8544 apr_array_header_t *children_with_mergeinfo,
8545 svn_depth_t depth,
8546 svn_boolean_t squelch_mergeinfo_notifications,
8547 merge_cmd_baton_t *merge_b,
8548 apr_pool_t *scratch_pool)
8549 {
8550 int i;
8551 svn_boolean_t is_rollback = (merged_range->start > merged_range->end);
8552 svn_boolean_t operative_merge;
8553
8554 /* Update the WC mergeinfo here to account for our new
8555 merges, minus any unresolved conflicts and skips. */
8556
8557 /* We need a scratch pool for iterations below. */
8558 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
8559
8560 svn_merge_range_t range = *merged_range;
8561
8562 assert(! merge_b->dry_run);
8563
8564 /* Regardless of what subtrees in MERGE_B->target->abspath might be missing
8565 could this merge have been operative? */
8566 operative_merge = subtree_touched_by_merge(merge_b->target->abspath,
8567 merge_b, iterpool);
8568
8569 /* If this couldn't be an operative merge then don't bother with
8570 the added complexity (and user confusion) of non-inheritable ranges.
8571 There is no harm in subtrees inheriting inoperative mergeinfo. */
8572 if (!operative_merge)
8573 range.inheritable = TRUE;
8574
8575 /* Remove absent children at or under MERGE_B->target->abspath from
8576 CHILDREN_WITH_MERGEINFO
8577 before we calculate the merges performed. */
8578 SVN_ERR(remove_absent_children(merge_b->target->abspath,
8579 children_with_mergeinfo));
8580
8581 /* Determine which subtrees of interest need mergeinfo recorded... */
8582 SVN_ERR(flag_subtrees_needing_mergeinfo(operative_merge, &range,
8583 children_with_mergeinfo,
8584 mergeinfo_fspath, depth,
8585 merge_b, iterpool));
8586
8587 /* ...and then record it. */
8588 for (i = 0; i < children_with_mergeinfo->nelts; i++)
8589 {
8590 const char *child_repos_path;
8591 const char *child_merge_src_fspath;
8592 svn_rangelist_t *child_merge_rangelist;
8593 apr_hash_t *child_merges;
8594 svn_client__merge_path_t *child =
8595 APR_ARRAY_IDX(children_with_mergeinfo, i,
8596 svn_client__merge_path_t *);
8597 SVN_ERR_ASSERT(child);
8598
8599 svn_pool_clear(iterpool);
8600
8601 if (child->record_mergeinfo)
8602 {
8603 child_repos_path = svn_dirent_skip_ancestor(merge_b->target->abspath,
8604 child->abspath);
8605 SVN_ERR_ASSERT(child_repos_path != NULL);
8606 child_merge_src_fspath = svn_fspath__join(mergeinfo_fspath,
8607 child_repos_path,
8608 iterpool);
8609 /* Filter any ranges from each child's natural history before
8610 setting mergeinfo describing the merge. */
8611 SVN_ERR(filter_natural_history_from_mergeinfo(
8612 &child_merge_rangelist, child_merge_src_fspath,
8613 child->implicit_mergeinfo, &range, iterpool));
8614
8615 if (child_merge_rangelist->nelts == 0)
8616 continue;
8617
8618 if (!squelch_mergeinfo_notifications)
8619 {
8620 /* If the merge source has a gap, then don't mention
8621 those gap revisions in the notification. */
8622 remove_source_gap(&range, merge_b->implicit_src_gap);
8623 notify_mergeinfo_recording(child->abspath, &range,
8624 merge_b->ctx, iterpool);
8625 }
8626
8627 /* If we are here we know we will be recording some mergeinfo, but
8628 before we do, set override mergeinfo on skipped paths so they
8629 don't incorrectly inherit the mergeinfo we are about to set. */
8630 if (i == 0)
8631 SVN_ERR(record_skips_in_mergeinfo(mergeinfo_fspath,
8632 child_merge_rangelist,
8633 is_rollback, merge_b, iterpool));
8634
8635 /* We may need to record non-inheritable mergeinfo that applies
8636 only to CHILD->ABSPATH. */
8637 if (child->record_noninheritable)
8638 svn_rangelist__set_inheritance(child_merge_rangelist, FALSE);
8639
8640 /* If CHILD has inherited mergeinfo set it before
8641 recording the first merge range. */
8642 if (child->inherited_mergeinfo)
8643 SVN_ERR(svn_client__record_wc_mergeinfo(
8644 child->abspath,
8645 child->pre_merge_mergeinfo,
8646 FALSE, merge_b->ctx,
8647 iterpool));
8648 if (merge_b->implicit_src_gap)
8649 {
8650 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
8651 so it will work with the svn_rangelist_remove API. */
8652 if (is_rollback)
8653 SVN_ERR(svn_rangelist_reverse(child_merge_rangelist,
8654 iterpool));
8655
8656 SVN_ERR(svn_rangelist_remove(&child_merge_rangelist,
8657 merge_b->implicit_src_gap,
8658 child_merge_rangelist, FALSE,
8659 iterpool));
8660 if (is_rollback)
8661 SVN_ERR(svn_rangelist_reverse(child_merge_rangelist,
8662 iterpool));
8663 }
8664
8665 child_merges = apr_hash_make(iterpool);
8666
8667 /* The short story:
8668
8669 If we are describing a forward merge, then the naive mergeinfo
8670 defined by MERGE_SOURCE_PATH:MERGED_RANGE->START:
8671 MERGE_SOURCE_PATH:MERGED_RANGE->END may contain non-existent
8672 path-revs or may describe other lines of history. We must
8673 remove these invalid portion(s) before recording mergeinfo
8674 describing the merge.
8675
8676 The long story:
8677
8678 If CHILD is the merge target we know that
8679 MERGE_SOURCE_PATH:MERGED_RANGE->END exists. Further, if there
8680 were no copies in MERGE_SOURCE_PATH's history going back to
8681 RANGE->START then we know that
8682 MERGE_SOURCE_PATH:MERGED_RANGE->START exists too and the two
8683 describe an unbroken line of history, and thus
8684 MERGE_SOURCE_PATH:MERGED_RANGE->START:
8685 MERGE_SOURCE_PATH:MERGED_RANGE->END is a valid description of
8686 the merge -- see normalize_merge_sources() and the global comment
8687 'MERGEINFO MERGE SOURCE NORMALIZATION'.
8688
8689 However, if there *was* a copy, then
8690 MERGE_SOURCE_PATH:MERGED_RANGE->START doesn't exist or is
8691 unrelated to MERGE_SOURCE_PATH:MERGED_RANGE->END. Also, we
8692 don't know if (MERGE_SOURCE_PATH:MERGED_RANGE->START)+1 through
8693 (MERGE_SOURCE_PATH:MERGED_RANGE->END)-1 actually exist.
8694
8695 If CHILD is a subtree of the merge target, then nothing is
8696 guaranteed beyond the fact that MERGE_SOURCE_PATH exists at
8697 MERGED_RANGE->END. */
8698 if ((!merge_b->record_only || merge_b->reintegrate_merge)
8699 && (!is_rollback))
8700 {
8701 svn_error_t *err;
8702 svn_mergeinfo_t subtree_history_as_mergeinfo;
8703 svn_rangelist_t *child_merge_src_rangelist;
8704 svn_client__pathrev_t *subtree_mergeinfo_pathrev
8705 = svn_client__pathrev_create_with_relpath(
8706 merge_b->target->loc.repos_root_url,
8707 merge_b->target->loc.repos_uuid,
8708 merged_range->end, child_merge_src_fspath + 1,
8709 iterpool);
8710
8711 /* Confirm that the naive mergeinfo we want to set on
8712 CHILD->ABSPATH both exists and is part of
8713 (MERGE_SOURCE_PATH+CHILD_REPOS_PATH)@MERGED_RANGE->END's
8714 history. */
8715 /* We know MERGED_RANGE->END is younger than MERGE_RANGE->START
8716 because we only do this for forward merges. */
8717 err = svn_client__get_history_as_mergeinfo(
8718 &subtree_history_as_mergeinfo, NULL,
8719 subtree_mergeinfo_pathrev,
8720 merged_range->end, merged_range->start,
8721 merge_b->ra_session2, merge_b->ctx, iterpool);
8722
8723 /* If CHILD is a subtree it may have been deleted prior to
8724 MERGED_RANGE->END so the above call to get its history
8725 will fail. */
8726 if (err)
8727 {
8728 if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
8729 return svn_error_trace(err);
8730 svn_error_clear(err);
8731 }
8732 else
8733 {
8734 child_merge_src_rangelist = svn_hash_gets(
8735 subtree_history_as_mergeinfo,
8736 child_merge_src_fspath);
8737 SVN_ERR(svn_rangelist_intersect(&child_merge_rangelist,
8738 child_merge_rangelist,
8739 child_merge_src_rangelist,
8740 FALSE, iterpool));
8741 if (child->record_noninheritable)
8742 svn_rangelist__set_inheritance(child_merge_rangelist,
8743 FALSE);
8744 }
8745 }
8746
8747 svn_hash_sets(child_merges, child->abspath, child_merge_rangelist);
8748 SVN_ERR(update_wc_mergeinfo(result_catalog,
8749 child->abspath,
8750 child_merge_src_fspath,
8751 child_merges, is_rollback,
8752 merge_b->ctx, iterpool));
8753
8754 /* Once is enough: We don't need to record mergeinfo describing
8755 the merge a second. If CHILD->ABSPATH is in
8756 MERGE_B->ADDED_ABSPATHS, we'll do just that, so remove the
8757 former from the latter. */
8758 svn_hash_sets(merge_b->added_abspaths, child->abspath, NULL);
8759 }
8760
8761 /* Elide explicit subtree mergeinfo whether or not we updated it. */
8762 if (i > 0)
8763 {
8764 svn_boolean_t in_switched_subtree = FALSE;
8765
8766 if (child->switched)
8767 in_switched_subtree = TRUE;
8768 else if (i > 1)
8769 {
8770 /* Check if CHILD is part of a switched subtree */
8771 svn_client__merge_path_t *parent;
8772 int j = i - 1;
8773 for (; j > 0; j--)
8774 {
8775 parent = APR_ARRAY_IDX(children_with_mergeinfo,
8776 j, svn_client__merge_path_t *);
8777 if (parent
8778 && parent->switched
8779 && svn_dirent_is_ancestor(parent->abspath,
8780 child->abspath))
8781 {
8782 in_switched_subtree = TRUE;
8783 break;
8784 }
8785 }
8786 }
8787
8788 /* Allow mergeinfo on switched subtrees to elide to the
8789 repository. Otherwise limit elision to the merge target
8790 for now. do_merge() will eventually try to
8791 elide that when the merge is complete. */
8792 SVN_ERR(svn_client__elide_mergeinfo(
8793 child->abspath,
8794 in_switched_subtree ? NULL : merge_b->target->abspath,
8795 merge_b->ctx, iterpool));
8796 }
8797 } /* (i = 0; i < notify_b->children_with_mergeinfo->nelts; i++) */
8798
8799 svn_pool_destroy(iterpool);
8800 return SVN_NO_ERROR;
8801 }
8802
8803 /* Helper for do_directory_merge().
8804
8805 Record mergeinfo describing a merge of
8806 MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
8807 MERGEINFO_FSPATH to each path in ADDED_ABSPATHS which has explicit
8808 mergeinfo or is the immediate child of a parent with explicit
8809 non-inheritable mergeinfo.
8810
8811 DEPTH, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS, are
8812 cascaded from do_directory_merge's arguments of the same names.
8813
8814 Note: This is intended to support forward merges only, i.e.
8815 MERGED_RANGE->START must be older than MERGED_RANGE->END.
8816 */
8817 static svn_error_t *
record_mergeinfo_for_added_subtrees(svn_merge_range_t * merged_range,const char * mergeinfo_fspath,svn_depth_t depth,svn_boolean_t squelch_mergeinfo_notifications,apr_hash_t * added_abspaths,merge_cmd_baton_t * merge_b,apr_pool_t * pool)8818 record_mergeinfo_for_added_subtrees(
8819 svn_merge_range_t *merged_range,
8820 const char *mergeinfo_fspath,
8821 svn_depth_t depth,
8822 svn_boolean_t squelch_mergeinfo_notifications,
8823 apr_hash_t *added_abspaths,
8824 merge_cmd_baton_t *merge_b,
8825 apr_pool_t *pool)
8826 {
8827 apr_pool_t *iterpool;
8828 apr_hash_index_t *hi;
8829
8830 /* If no paths were added by the merge then we have nothing to do. */
8831 if (!added_abspaths)
8832 return SVN_NO_ERROR;
8833
8834 SVN_ERR_ASSERT(merged_range->start < merged_range->end);
8835
8836 iterpool = svn_pool_create(pool);
8837 for (hi = apr_hash_first(pool, added_abspaths); hi; hi = apr_hash_next(hi))
8838 {
8839 const char *added_abspath = apr_hash_this_key(hi);
8840 const char *dir_abspath;
8841 svn_mergeinfo_t parent_mergeinfo;
8842 svn_mergeinfo_t added_path_mergeinfo;
8843
8844 svn_pool_clear(iterpool);
8845 dir_abspath = svn_dirent_dirname(added_abspath, iterpool);
8846
8847 /* Grab the added path's explicit mergeinfo. */
8848 SVN_ERR(svn_client__get_wc_mergeinfo(&added_path_mergeinfo, NULL,
8849 svn_mergeinfo_explicit,
8850 added_abspath, NULL, NULL, FALSE,
8851 merge_b->ctx, iterpool, iterpool));
8852
8853 /* If the added path doesn't have explicit mergeinfo, does its immediate
8854 parent have non-inheritable mergeinfo? */
8855 if (!added_path_mergeinfo)
8856 SVN_ERR(svn_client__get_wc_mergeinfo(&parent_mergeinfo, NULL,
8857 svn_mergeinfo_explicit,
8858 dir_abspath, NULL, NULL, FALSE,
8859 merge_b->ctx,
8860 iterpool, iterpool));
8861
8862 if (added_path_mergeinfo
8863 || svn_mergeinfo__is_noninheritable(parent_mergeinfo, iterpool))
8864 {
8865 svn_node_kind_t added_path_kind;
8866 svn_mergeinfo_t merge_mergeinfo;
8867 svn_mergeinfo_t adds_history_as_mergeinfo;
8868 svn_rangelist_t *rangelist;
8869 const char *rel_added_path;
8870 const char *added_path_mergeinfo_fspath;
8871 svn_client__pathrev_t *added_path_pathrev;
8872
8873 SVN_ERR(svn_wc_read_kind2(&added_path_kind, merge_b->ctx->wc_ctx,
8874 added_abspath, FALSE, FALSE, iterpool));
8875
8876 /* Calculate the naive mergeinfo describing the merge. */
8877 merge_mergeinfo = apr_hash_make(iterpool);
8878 rangelist = svn_rangelist__initialize(
8879 merged_range->start, merged_range->end,
8880 ((added_path_kind == svn_node_file)
8881 || (!(depth == svn_depth_infinity
8882 || depth == svn_depth_immediates))),
8883 iterpool);
8884
8885 /* Create the new mergeinfo path for added_path's mergeinfo.
8886 (added_abspath had better be a child of MERGE_B->target->abspath
8887 or something is *really* wrong.) */
8888 rel_added_path = svn_dirent_is_child(merge_b->target->abspath,
8889 added_abspath, iterpool);
8890 SVN_ERR_ASSERT(rel_added_path);
8891 added_path_mergeinfo_fspath = svn_fspath__join(mergeinfo_fspath,
8892 rel_added_path,
8893 iterpool);
8894 svn_hash_sets(merge_mergeinfo, added_path_mergeinfo_fspath,
8895 rangelist);
8896
8897 /* Don't add new mergeinfo to describe the merge if that mergeinfo
8898 contains non-existent merge sources.
8899
8900 We know that MERGEINFO_PATH/rel_added_path's history does not
8901 span MERGED_RANGE->START:MERGED_RANGE->END but rather that it
8902 was added at some revions greater than MERGED_RANGE->START
8903 (assuming this is a forward merge). It may have been added,
8904 deleted, and re-added many times. The point is that we cannot
8905 blindly apply the naive mergeinfo calculated above because it
8906 will describe non-existent merge sources. To avoid this we get
8907 take the intersection of the naive mergeinfo with
8908 MERGEINFO_PATH/rel_added_path's history. */
8909 added_path_pathrev = svn_client__pathrev_create_with_relpath(
8910 merge_b->target->loc.repos_root_url,
8911 merge_b->target->loc.repos_uuid,
8912 MAX(merged_range->start, merged_range->end),
8913 added_path_mergeinfo_fspath + 1, iterpool);
8914 SVN_ERR(svn_client__get_history_as_mergeinfo(
8915 &adds_history_as_mergeinfo, NULL,
8916 added_path_pathrev,
8917 MAX(merged_range->start, merged_range->end),
8918 MIN(merged_range->start, merged_range->end),
8919 merge_b->ra_session2, merge_b->ctx, iterpool));
8920
8921 SVN_ERR(svn_mergeinfo_intersect2(&merge_mergeinfo,
8922 merge_mergeinfo,
8923 adds_history_as_mergeinfo,
8924 FALSE, iterpool, iterpool));
8925
8926 /* Combine the explicit mergeinfo on the added path (if any)
8927 with the mergeinfo describing this merge. */
8928 if (added_path_mergeinfo)
8929 SVN_ERR(svn_mergeinfo_merge2(merge_mergeinfo,
8930 added_path_mergeinfo,
8931 iterpool, iterpool));
8932 SVN_ERR(svn_client__record_wc_mergeinfo(
8933 added_abspath, merge_mergeinfo,
8934 !squelch_mergeinfo_notifications, merge_b->ctx, iterpool));
8935 }
8936 }
8937 svn_pool_destroy(iterpool);
8938
8939 return SVN_NO_ERROR;
8940 }
8941 /* Baton structure for log_noop_revs. */
8942 typedef struct log_noop_baton_t
8943 {
8944 /* See the comment 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start
8945 of this file.*/
8946 apr_array_header_t *children_with_mergeinfo;
8947
8948 /* Absolute repository path of younger of the two merge sources
8949 being diffed. */
8950 const char *source_fspath;
8951
8952 /* The merge target. */
8953 const merge_target_t *target;
8954
8955 /* Initially empty rangelists allocated in POOL. The rangelists are
8956 * populated across multiple invocations of log_noop_revs(). */
8957 svn_rangelist_t *operative_ranges;
8958 svn_rangelist_t *merged_ranges;
8959
8960 /* Pool to store the rangelists. */
8961 apr_pool_t *pool;
8962 } log_noop_baton_t;
8963
8964 /* Helper for log_noop_revs: Merge a svn_merge_range_t representation of
8965 REVISION into RANGELIST. New elements added to rangelist are allocated
8966 in RESULT_POOL.
8967
8968 This is *not* a general purpose rangelist merge but a special replacement
8969 for svn_rangelist_merge when REVISION is guaranteed to be younger than any
8970 element in RANGELIST. svn_rangelist_merge is O(n) worst-case (i.e. when
8971 all the ranges in output rangelist are older than the incoming changes).
8972 This turns the special case of a single incoming younger range into O(1).
8973 */
8974 static svn_error_t *
rangelist_merge_revision(svn_rangelist_t * rangelist,svn_revnum_t revision,apr_pool_t * result_pool)8975 rangelist_merge_revision(svn_rangelist_t *rangelist,
8976 svn_revnum_t revision,
8977 apr_pool_t *result_pool)
8978 {
8979 svn_merge_range_t *new_range;
8980 if (rangelist->nelts)
8981 {
8982 svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
8983 svn_merge_range_t *);
8984 if (range->end == revision - 1)
8985 {
8986 /* REVISION is adjacent to the youngest range in RANGELIST
8987 so we can simply expand that range to encompass REVISION. */
8988 range->end = revision;
8989 return SVN_NO_ERROR;
8990 }
8991 }
8992 new_range = apr_palloc(result_pool, sizeof(*new_range));
8993 new_range->start = revision - 1;
8994 new_range->end = revision;
8995 new_range->inheritable = TRUE;
8996
8997 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = new_range;
8998
8999 return SVN_NO_ERROR;
9000 }
9001
9002 /* Implements the svn_log_entry_receiver_t interface.
9003
9004 BATON is an log_noop_baton_t *.
9005
9006 Add LOG_ENTRY->REVISION to BATON->OPERATIVE_RANGES.
9007
9008 If LOG_ENTRY->REVISION has already been fully merged to
9009 BATON->target->abspath per the mergeinfo in BATON->CHILDREN_WITH_MERGEINFO,
9010 then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES.
9011
9012 Use SCRATCH_POOL for temporary allocations. Allocate additions to
9013 BATON->MERGED_RANGES and BATON->OPERATIVE_RANGES in BATON->POOL.
9014
9015 Note: This callback must be invoked from oldest LOG_ENTRY->REVISION
9016 to youngest LOG_ENTRY->REVISION -- see rangelist_merge_revision().
9017 */
9018 static svn_error_t *
log_noop_revs(void * baton,svn_log_entry_t * log_entry,apr_pool_t * scratch_pool)9019 log_noop_revs(void *baton,
9020 svn_log_entry_t *log_entry,
9021 apr_pool_t *scratch_pool)
9022 {
9023 log_noop_baton_t *log_gap_baton = baton;
9024 apr_hash_index_t *hi;
9025 svn_revnum_t revision;
9026 svn_boolean_t log_entry_rev_required = FALSE;
9027
9028 revision = log_entry->revision;
9029
9030 /* It's possible that authz restrictions on the merge source prevent us
9031 from knowing about any of the changes for LOG_ENTRY->REVISION. */
9032 if (!log_entry->changed_paths2)
9033 return SVN_NO_ERROR;
9034
9035 /* Unconditionally add LOG_ENTRY->REVISION to BATON->OPERATIVE_MERGES. */
9036 SVN_ERR(rangelist_merge_revision(log_gap_baton->operative_ranges,
9037 revision,
9038 log_gap_baton->pool));
9039
9040 /* Examine each path affected by LOG_ENTRY->REVISION. If the explicit or
9041 inherited mergeinfo for *all* of the corresponding paths under
9042 BATON->target->abspath reflects that LOG_ENTRY->REVISION has been
9043 merged, then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES. */
9044 for (hi = apr_hash_first(scratch_pool, log_entry->changed_paths2);
9045 hi;
9046 hi = apr_hash_next(hi))
9047 {
9048 const char *fspath = apr_hash_this_key(hi);
9049 const char *rel_path;
9050 const char *cwmi_abspath;
9051 svn_rangelist_t *paths_explicit_rangelist = NULL;
9052 svn_boolean_t mergeinfo_inherited = FALSE;
9053
9054 /* Adjust REL_PATH so it is relative to the merge source then use it to
9055 calculate what path in the merge target would be affected by this
9056 revision. */
9057 rel_path = svn_fspath__skip_ancestor(log_gap_baton->source_fspath,
9058 fspath);
9059 /* Is PATH even within the merge target? If it isn't we
9060 can disregard it altogether. */
9061 if (rel_path == NULL)
9062 continue;
9063 cwmi_abspath = svn_dirent_join(log_gap_baton->target->abspath,
9064 rel_path, scratch_pool);
9065
9066 /* Find any explicit or inherited mergeinfo for PATH. */
9067 while (!log_entry_rev_required)
9068 {
9069 svn_client__merge_path_t *child = get_child_with_mergeinfo(
9070 log_gap_baton->children_with_mergeinfo, cwmi_abspath);
9071
9072 if (child && child->pre_merge_mergeinfo)
9073 {
9074 /* Found some explicit mergeinfo, grab any ranges
9075 for PATH. */
9076 paths_explicit_rangelist =
9077 svn_hash_gets(child->pre_merge_mergeinfo, fspath);
9078 break;
9079 }
9080
9081 if (cwmi_abspath[0] == '\0'
9082 || svn_dirent_is_root(cwmi_abspath, strlen(cwmi_abspath))
9083 || strcmp(log_gap_baton->target->abspath, cwmi_abspath) == 0)
9084 {
9085 /* Can't crawl any higher. */
9086 break;
9087 }
9088
9089 /* Didn't find anything so crawl up to the parent. */
9090 cwmi_abspath = svn_dirent_dirname(cwmi_abspath, scratch_pool);
9091 fspath = svn_fspath__dirname(fspath, scratch_pool);
9092
9093 /* At this point *if* we find mergeinfo it will be inherited. */
9094 mergeinfo_inherited = TRUE;
9095 }
9096
9097 if (paths_explicit_rangelist)
9098 {
9099 svn_rangelist_t *intersecting_range;
9100 svn_rangelist_t *rangelist;
9101
9102 rangelist = svn_rangelist__initialize(revision - 1, revision, TRUE,
9103 scratch_pool);
9104
9105 /* If PATH inherited mergeinfo we must consider inheritance in the
9106 event the inherited mergeinfo is actually non-inheritable. */
9107 SVN_ERR(svn_rangelist_intersect(&intersecting_range,
9108 paths_explicit_rangelist,
9109 rangelist,
9110 mergeinfo_inherited, scratch_pool));
9111
9112 if (intersecting_range->nelts == 0)
9113 log_entry_rev_required = TRUE;
9114 }
9115 else
9116 {
9117 log_entry_rev_required = TRUE;
9118 }
9119 }
9120
9121 if (!log_entry_rev_required)
9122 SVN_ERR(rangelist_merge_revision(log_gap_baton->merged_ranges,
9123 revision,
9124 log_gap_baton->pool));
9125
9126 return SVN_NO_ERROR;
9127 }
9128
9129 /* Helper for do_directory_merge().
9130
9131 SOURCE is cascaded from the argument of the same name in
9132 do_directory_merge(). TARGET is the merge target. RA_SESSION is the
9133 session for SOURCE->loc2.
9134
9135 Find all the ranges required by subtrees in
9136 CHILDREN_WITH_MERGEINFO that are *not* required by
9137 TARGET->abspath (i.e. CHILDREN_WITH_MERGEINFO[0]). If such
9138 ranges exist, then find any subset of ranges which, if merged, would be
9139 inoperative. Finally, if any inoperative ranges are found then remove
9140 these ranges from all of the subtree's REMAINING_RANGES.
9141
9142 This function should only be called when honoring mergeinfo during
9143 forward merges (i.e. SOURCE->rev1 < SOURCE->rev2).
9144 */
9145 static svn_error_t *
remove_noop_subtree_ranges(const merge_source_t * source,const merge_target_t * target,svn_ra_session_t * ra_session,apr_array_header_t * children_with_mergeinfo,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9146 remove_noop_subtree_ranges(const merge_source_t *source,
9147 const merge_target_t *target,
9148 svn_ra_session_t *ra_session,
9149 apr_array_header_t *children_with_mergeinfo,
9150 apr_pool_t *result_pool,
9151 apr_pool_t *scratch_pool)
9152 {
9153 /* ### Do we need to check that we are at a uniform working revision? */
9154 int i;
9155 svn_client__merge_path_t *root_child =
9156 APR_ARRAY_IDX(children_with_mergeinfo, 0, svn_client__merge_path_t *);
9157 svn_rangelist_t *requested_ranges;
9158 svn_rangelist_t *subtree_gap_ranges;
9159 svn_rangelist_t *subtree_remaining_ranges;
9160 log_noop_baton_t log_gap_baton;
9161 svn_merge_range_t *oldest_gap_rev;
9162 svn_merge_range_t *youngest_gap_rev;
9163 svn_rangelist_t *inoperative_ranges;
9164 apr_pool_t *iterpool;
9165 const char *longest_common_subtree_ancestor = NULL;
9166 svn_error_t *err;
9167
9168 assert(session_url_is(ra_session, source->loc2->url, scratch_pool));
9169
9170 /* This function is only intended to work with forward merges. */
9171 if (source->loc1->rev > source->loc2->rev)
9172 return SVN_NO_ERROR;
9173
9174 /* Another easy out: There are no subtrees. */
9175 if (children_with_mergeinfo->nelts < 2)
9176 return SVN_NO_ERROR;
9177
9178 subtree_remaining_ranges = apr_array_make(scratch_pool, 1,
9179 sizeof(svn_merge_range_t *));
9180
9181 /* Given the requested merge of SOURCE->rev1:rev2 might there be any
9182 part of this range required for subtrees but not for the target? */
9183 requested_ranges = svn_rangelist__initialize(MIN(source->loc1->rev,
9184 source->loc2->rev),
9185 MAX(source->loc1->rev,
9186 source->loc2->rev),
9187 TRUE, scratch_pool);
9188 SVN_ERR(svn_rangelist_remove(&subtree_gap_ranges,
9189 root_child->remaining_ranges,
9190 requested_ranges, FALSE, scratch_pool));
9191
9192 /* Early out, nothing to operate on */
9193 if (!subtree_gap_ranges->nelts)
9194 return SVN_NO_ERROR;
9195
9196 /* Create a rangelist describing every range required across all subtrees. */
9197 iterpool = svn_pool_create(scratch_pool);
9198 for (i = 1; i < children_with_mergeinfo->nelts; i++)
9199 {
9200 svn_client__merge_path_t *child =
9201 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
9202
9203 svn_pool_clear(iterpool);
9204
9205 /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
9206 if (child->remaining_ranges && child->remaining_ranges->nelts)
9207 {
9208 /* Issue #4269: Keep track of the longest common ancestor of all the
9209 subtrees which require merges. This may be a child of
9210 TARGET->ABSPATH, which will allow us to narrow the log request
9211 below. */
9212 if (longest_common_subtree_ancestor)
9213 longest_common_subtree_ancestor = svn_dirent_get_longest_ancestor(
9214 longest_common_subtree_ancestor, child->abspath, scratch_pool);
9215 else
9216 longest_common_subtree_ancestor = child->abspath;
9217
9218 SVN_ERR(svn_rangelist_merge2(subtree_remaining_ranges,
9219 child->remaining_ranges,
9220 scratch_pool, iterpool));
9221 }
9222 }
9223 svn_pool_destroy(iterpool);
9224
9225 /* It's possible that none of the subtrees had any remaining ranges. */
9226 if (!subtree_remaining_ranges->nelts)
9227 return SVN_NO_ERROR;
9228
9229 /* Ok, *finally* we can answer what part(s) of SOURCE->rev1:rev2 are
9230 required for the subtrees but not the target. */
9231 SVN_ERR(svn_rangelist_intersect(&subtree_gap_ranges,
9232 subtree_gap_ranges,
9233 subtree_remaining_ranges, FALSE,
9234 scratch_pool));
9235
9236 /* Another early out */
9237 if (!subtree_gap_ranges->nelts)
9238 return SVN_NO_ERROR;
9239
9240 /* One or more subtrees need some revisions that the target doesn't need.
9241 Use log to determine if any of these revisions are inoperative. */
9242 oldest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges, 0, svn_merge_range_t *);
9243 youngest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges,
9244 subtree_gap_ranges->nelts - 1, svn_merge_range_t *);
9245
9246 /* Set up the log baton. */
9247 log_gap_baton.children_with_mergeinfo = children_with_mergeinfo;
9248 log_gap_baton.source_fspath
9249 = svn_client__pathrev_fspath(source->loc2, result_pool);
9250 log_gap_baton.target = target;
9251 log_gap_baton.merged_ranges = apr_array_make(scratch_pool, 0,
9252 sizeof(svn_revnum_t *));
9253 log_gap_baton.operative_ranges = apr_array_make(scratch_pool, 0,
9254 sizeof(svn_revnum_t *));
9255 log_gap_baton.pool = svn_pool_create(scratch_pool);
9256
9257 /* Find the longest common ancestor of all subtrees relative to
9258 RA_SESSION's URL. */
9259 if (longest_common_subtree_ancestor)
9260 longest_common_subtree_ancestor =
9261 svn_dirent_skip_ancestor(target->abspath,
9262 longest_common_subtree_ancestor);
9263 else
9264 longest_common_subtree_ancestor = "";
9265
9266 /* Invoke the svn_log_entry_receiver_t receiver log_noop_revs() from
9267 oldest to youngest. The receiver is optimized to add ranges to
9268 log_gap_baton.merged_ranges and log_gap_baton.operative_ranges, but
9269 requires that the revs arrive oldest to youngest -- see log_noop_revs()
9270 and rangelist_merge_revision(). */
9271 err = get_log(ra_session, longest_common_subtree_ancestor,
9272 oldest_gap_rev->start + 1, youngest_gap_rev->end, TRUE,
9273 log_noop_revs, &log_gap_baton, scratch_pool);
9274
9275 /* It's possible that the only subtrees with mergeinfo in TARGET don't have
9276 any corresponding subtree in SOURCE between SOURCE->REV1 < SOURCE->REV2.
9277 So it's also possible that we may ask for the logs of non-existent paths.
9278 If we do, then assume that no subtree requires any ranges that are not
9279 already required by the TARGET. */
9280 if (err)
9281 {
9282 if (err->apr_err != SVN_ERR_FS_NOT_FOUND
9283 && longest_common_subtree_ancestor[0] != '\0')
9284 return svn_error_trace(err);
9285
9286 /* Asked about a non-existent subtree in SOURCE. */
9287 svn_error_clear(err);
9288 log_gap_baton.merged_ranges =
9289 svn_rangelist__initialize(oldest_gap_rev->start,
9290 youngest_gap_rev->end,
9291 TRUE, scratch_pool);
9292 }
9293 else
9294 {
9295 inoperative_ranges = svn_rangelist__initialize(oldest_gap_rev->start,
9296 youngest_gap_rev->end,
9297 TRUE, scratch_pool);
9298 SVN_ERR(svn_rangelist_remove(&(inoperative_ranges),
9299 log_gap_baton.operative_ranges,
9300 inoperative_ranges, FALSE, scratch_pool));
9301 SVN_ERR(svn_rangelist_merge2(log_gap_baton.merged_ranges, inoperative_ranges,
9302 scratch_pool, scratch_pool));
9303 }
9304
9305 for (i = 1; i < children_with_mergeinfo->nelts; i++)
9306 {
9307 svn_client__merge_path_t *child =
9308 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
9309
9310 /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
9311 if (child->remaining_ranges && child->remaining_ranges->nelts)
9312 {
9313 /* Remove inoperative ranges from all children so we don't perform
9314 inoperative editor drives. */
9315 SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
9316 log_gap_baton.merged_ranges,
9317 child->remaining_ranges,
9318 FALSE, result_pool));
9319 }
9320 }
9321
9322 svn_pool_destroy(log_gap_baton.pool);
9323
9324 return SVN_NO_ERROR;
9325 }
9326
9327 /* Perform a merge of changes in SOURCE to the working copy path
9328 TARGET_ABSPATH. Both URLs in SOURCE, and TARGET_ABSPATH all represent
9329 directories -- for the single file case, the caller should use
9330 do_file_merge().
9331
9332 CHILDREN_WITH_MERGEINFO and MERGE_B describe the merge being performed
9333 As this function is for a mergeinfo-aware merge, SOURCE->ancestral
9334 should be TRUE, and SOURCE->loc1 must be a historical ancestor of
9335 SOURCE->loc2, or vice-versa (see `MERGEINFO MERGE SOURCE NORMALIZATION'
9336 for more requirements around SOURCE).
9337
9338 Mergeinfo changes will be recorded unless MERGE_B->dry_run is true.
9339
9340 If mergeinfo is being recorded, SQUELCH_MERGEINFO_NOTIFICATIONS is FALSE,
9341 and MERGE_B->CTX->NOTIFY_FUNC2 is not NULL, then call
9342 MERGE_B->CTX->NOTIFY_FUNC2 with MERGE_B->CTX->NOTIFY_BATON2 and a
9343 svn_wc_notify_merge_record_info_begin notification before any mergeinfo
9344 changes are made to describe the merge performed.
9345
9346 If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
9347 is not NULL, then don't record the new mergeinfo on the WC, but instead
9348 record it in RESULT_CATALOG, where the keys are absolute working copy
9349 paths and the values are the new mergeinfos for each. Allocate additions
9350 to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
9351
9352 Handle DEPTH as documented for svn_client_merge5().
9353
9354 CONFLICT_REPORT is as documented for do_directory_merge().
9355
9356 Perform any temporary allocations in SCRATCH_POOL.
9357
9358 NOTE: This is a wrapper around drive_merge_report_editor() which
9359 handles the complexities inherent to situations where a given
9360 directory's children may have intersecting merges (because they
9361 meet one or more of the criteria described in get_mergeinfo_paths()).
9362 */
9363 static svn_error_t *
do_mergeinfo_aware_dir_merge(svn_mergeinfo_catalog_t result_catalog,single_range_conflict_report_t ** conflict_report,const merge_source_t * source,const char * target_abspath,apr_array_header_t * children_with_mergeinfo,const svn_diff_tree_processor_t * processor,svn_depth_t depth,svn_boolean_t squelch_mergeinfo_notifications,merge_cmd_baton_t * merge_b,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9364 do_mergeinfo_aware_dir_merge(svn_mergeinfo_catalog_t result_catalog,
9365 single_range_conflict_report_t **conflict_report,
9366 const merge_source_t *source,
9367 const char *target_abspath,
9368 apr_array_header_t *children_with_mergeinfo,
9369 const svn_diff_tree_processor_t *processor,
9370 svn_depth_t depth,
9371 svn_boolean_t squelch_mergeinfo_notifications,
9372 merge_cmd_baton_t *merge_b,
9373 apr_pool_t *result_pool,
9374 apr_pool_t *scratch_pool)
9375 {
9376 /* The range defining the mergeinfo we will record to describe the merge
9377 (assuming we are recording mergeinfo
9378
9379 Note: This may be a subset of SOURCE->rev1:rev2 if
9380 populate_remaining_ranges() determines that some part of
9381 SOURCE->rev1:rev2 has already been wholly merged to TARGET_ABSPATH.
9382 Also, the actual editor drive(s) may be a subset of RANGE, if
9383 remove_noop_subtree_ranges() and/or fix_deleted_subtree_ranges()
9384 further tweak things. */
9385 svn_merge_range_t range;
9386
9387 svn_ra_session_t *ra_session;
9388 svn_client__merge_path_t *target_merge_path;
9389 svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
9390
9391 SVN_ERR_ASSERT(source->ancestral);
9392
9393 /*** If we get here, we're dealing with related sources from the
9394 same repository as the target -- merge tracking might be
9395 happenin'! ***/
9396
9397 *conflict_report = NULL;
9398
9399 /* Point our RA_SESSION to the URL of our youngest merge source side. */
9400 ra_session = is_rollback ? merge_b->ra_session1 : merge_b->ra_session2;
9401
9402 /* Fill CHILDREN_WITH_MERGEINFO with child paths (const
9403 svn_client__merge_path_t *) which might have intersecting merges
9404 because they meet one or more of the criteria described in
9405 get_mergeinfo_paths(). Here the paths are arranged in a depth
9406 first order. */
9407 SVN_ERR(get_mergeinfo_paths(children_with_mergeinfo,
9408 merge_b->target, depth,
9409 merge_b->dry_run, merge_b->same_repos,
9410 merge_b->ctx, scratch_pool, scratch_pool));
9411
9412 /* The first item from the CHILDREN_WITH_MERGEINFO is always
9413 the target thanks to depth-first ordering. */
9414 target_merge_path = APR_ARRAY_IDX(children_with_mergeinfo, 0,
9415 svn_client__merge_path_t *);
9416
9417 /* If we are honoring mergeinfo, then for each item in
9418 CHILDREN_WITH_MERGEINFO, we need to calculate what needs to be
9419 merged, and then merge it. Otherwise, we just merge what we were asked
9420 to merge across the whole tree. */
9421 SVN_ERR(populate_remaining_ranges(children_with_mergeinfo,
9422 source, ra_session,
9423 merge_b, scratch_pool, scratch_pool));
9424
9425 /* Always start with a range which describes the most inclusive merge
9426 possible, i.e. SOURCE->rev1:rev2. */
9427 range.start = source->loc1->rev;
9428 range.end = source->loc2->rev;
9429 range.inheritable = TRUE;
9430
9431 if (!merge_b->reintegrate_merge)
9432 {
9433 svn_revnum_t new_range_start, start_rev;
9434 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
9435
9436 /* The merge target TARGET_ABSPATH and/or its subtrees may not need all
9437 of SOURCE->rev1:rev2 applied. So examine
9438 CHILDREN_WITH_MERGEINFO to find the oldest starting
9439 revision that actually needs to be merged (for reverse merges this is
9440 the youngest starting revision).
9441
9442 We'll do this twice, right now for the start of the mergeinfo we will
9443 ultimately record to describe this merge and then later for the
9444 start of the actual editor drive. */
9445 new_range_start = get_most_inclusive_rev(
9446 children_with_mergeinfo, is_rollback, TRUE);
9447 if (SVN_IS_VALID_REVNUM(new_range_start))
9448 range.start = new_range_start;
9449
9450 /* Remove inoperative ranges from any subtrees' remaining_ranges
9451 to spare the expense of noop editor drives. */
9452 if (!is_rollback)
9453 SVN_ERR(remove_noop_subtree_ranges(source, merge_b->target,
9454 ra_session,
9455 children_with_mergeinfo,
9456 scratch_pool, iterpool));
9457
9458 /* Adjust subtrees' remaining_ranges to deal with issue #3067:
9459 * "subtrees that don't exist at the start or end of a merge range
9460 * shouldn't break the merge". */
9461 SVN_ERR(fix_deleted_subtree_ranges(source, merge_b->target,
9462 ra_session,
9463 children_with_mergeinfo,
9464 merge_b->ctx, scratch_pool, iterpool));
9465
9466 /* remove_noop_subtree_ranges() and/or fix_deleted_subtree_range()
9467 may have further refined the starting revision for our editor
9468 drive. */
9469 start_rev =
9470 get_most_inclusive_rev(children_with_mergeinfo,
9471 is_rollback, TRUE);
9472
9473 /* Is there anything to merge? */
9474 if (SVN_IS_VALID_REVNUM(start_rev))
9475 {
9476 /* Now examine CHILDREN_WITH_MERGEINFO to find the oldest
9477 ending revision that actually needs to be merged (for reverse
9478 merges this is the youngest ending revision). */
9479 svn_revnum_t end_rev =
9480 get_most_inclusive_rev(children_with_mergeinfo,
9481 is_rollback, FALSE);
9482
9483 /* While END_REV is valid, do the following:
9484
9485 1. Tweak each CHILDREN_WITH_MERGEINFO element so that
9486 the element's remaining_ranges member has as its first element
9487 a range that ends with end_rev.
9488
9489 2. Starting with start_rev, call drive_merge_report_editor()
9490 on MERGE_B->target->abspath for start_rev:end_rev.
9491
9492 3. Remove the first element from each
9493 CHILDREN_WITH_MERGEINFO element's remaining_ranges
9494 member.
9495
9496 4. Again examine CHILDREN_WITH_MERGEINFO to find the most
9497 inclusive starting revision that actually needs to be merged and
9498 update start_rev. This prevents us from needlessly contacting the
9499 repository and doing a diff where we describe the entire target
9500 tree as *not* needing any of the requested range. This can happen
9501 whenever we have mergeinfo with gaps in it for the merge source.
9502
9503 5. Again examine CHILDREN_WITH_MERGEINFO to find the most
9504 inclusive ending revision that actually needs to be merged and
9505 update end_rev.
9506
9507 6. Lather, rinse, repeat.
9508 */
9509
9510 while (end_rev != SVN_INVALID_REVNUM)
9511 {
9512 merge_source_t *real_source;
9513 svn_merge_range_t *first_target_range
9514 = (target_merge_path->remaining_ranges->nelts == 0 ? NULL
9515 : APR_ARRAY_IDX(target_merge_path->remaining_ranges, 0,
9516 svn_merge_range_t *));
9517
9518 /* Issue #3324: Stop editor abuse! Don't call
9519 drive_merge_report_editor() in such a way that we request an
9520 editor with svn_client__get_diff_editor() for some rev X,
9521 then call svn_ra_do_diff3() for some revision Y, and then
9522 call reporter->set_path(PATH=="") to set the root revision
9523 for the editor drive to revision Z where
9524 (X != Z && X < Z < Y). This is bogus because the server will
9525 send us the diff between X:Y but the client is expecting the
9526 diff between Y:Z. See issue #3324 for full details on the
9527 problems this can cause. */
9528 if (first_target_range
9529 && start_rev != first_target_range->start)
9530 {
9531 if (is_rollback)
9532 {
9533 if (end_rev < first_target_range->start)
9534 end_rev = first_target_range->start;
9535 }
9536 else
9537 {
9538 if (end_rev > first_target_range->start)
9539 end_rev = first_target_range->start;
9540 }
9541 }
9542
9543 svn_pool_clear(iterpool);
9544
9545 SVN_ERR(slice_remaining_ranges(children_with_mergeinfo,
9546 is_rollback, end_rev,
9547 scratch_pool));
9548
9549 /* Reset variables that must be reset for every drive */
9550 merge_b->notify_begin.last_abspath = NULL;
9551
9552 real_source = subrange_source(source, start_rev, end_rev, iterpool);
9553 SVN_ERR(drive_merge_report_editor(
9554 merge_b->target->abspath,
9555 real_source,
9556 children_with_mergeinfo,
9557 processor,
9558 depth,
9559 merge_b,
9560 iterpool));
9561
9562 /* If any paths picked up explicit mergeinfo as a result of
9563 the merge we need to make sure any mergeinfo those paths
9564 inherited is recorded and then add these paths to
9565 CHILDREN_WITH_MERGEINFO.*/
9566 SVN_ERR(process_children_with_new_mergeinfo(
9567 merge_b, children_with_mergeinfo,
9568 scratch_pool));
9569
9570 /* If any subtrees had their explicit mergeinfo deleted as a
9571 result of the merge then remove these paths from
9572 CHILDREN_WITH_MERGEINFO since there is no need
9573 to consider these subtrees for subsequent editor drives
9574 nor do we want to record mergeinfo on them describing
9575 the merge itself. */
9576 SVN_ERR(remove_children_with_deleted_mergeinfo(
9577 merge_b, children_with_mergeinfo));
9578
9579 /* Prepare for the next iteration (if any). */
9580 SVN_ERR(remove_first_range_from_remaining_ranges(
9581 end_rev, children_with_mergeinfo, scratch_pool));
9582
9583 /* If we raised any conflicts, break out and report how much
9584 we have merged. */
9585 if (is_path_conflicted_by_merge(merge_b))
9586 {
9587 merge_source_t *remaining_range = NULL;
9588
9589 if (real_source->loc2->rev != source->loc2->rev)
9590 remaining_range = subrange_source(source,
9591 real_source->loc2->rev,
9592 source->loc2->rev,
9593 scratch_pool);
9594 *conflict_report = single_range_conflict_report_create(
9595 real_source, remaining_range,
9596 result_pool);
9597
9598 range.end = end_rev;
9599 break;
9600 }
9601
9602 start_rev =
9603 get_most_inclusive_rev(children_with_mergeinfo,
9604 is_rollback, TRUE);
9605 end_rev =
9606 get_most_inclusive_rev(children_with_mergeinfo,
9607 is_rollback, FALSE);
9608 }
9609 }
9610 svn_pool_destroy(iterpool);
9611 }
9612 else
9613 {
9614 if (!merge_b->record_only)
9615 {
9616 /* Reset the last notification path so that subsequent cherry
9617 picked revision ranges will be notified upon subsequent
9618 operative merge. */
9619 merge_b->notify_begin.last_abspath = NULL;
9620
9621 SVN_ERR(drive_merge_report_editor(merge_b->target->abspath,
9622 source,
9623 NULL,
9624 processor,
9625 depth,
9626 merge_b,
9627 scratch_pool));
9628 }
9629 }
9630
9631 /* Record mergeinfo where appropriate.*/
9632 if (RECORD_MERGEINFO(merge_b))
9633 {
9634 const svn_client__pathrev_t *primary_src
9635 = is_rollback ? source->loc1 : source->loc2;
9636 const char *mergeinfo_path
9637 = svn_client__pathrev_fspath(primary_src, scratch_pool);
9638
9639 SVN_ERR(record_mergeinfo_for_dir_merge(result_catalog,
9640 &range,
9641 mergeinfo_path,
9642 children_with_mergeinfo,
9643 depth,
9644 squelch_mergeinfo_notifications,
9645 merge_b,
9646 scratch_pool));
9647
9648 /* If a path has an immediate parent with non-inheritable mergeinfo at
9649 this point, then it meets criteria 3 or 5 described in
9650 get_mergeinfo_paths' doc string. For paths which exist prior to a
9651 merge explicit mergeinfo has already been set. But for paths added
9652 during the merge this is not the case. The path might have explicit
9653 mergeinfo from the merge source, but no mergeinfo yet exists
9654 describing *this* merge. So the added path has either incomplete
9655 explicit mergeinfo or inherits incomplete mergeinfo from its
9656 immediate parent (if any, the parent might have only non-inheritable
9657 ranges in which case the path simply inherits empty mergeinfo).
9658
9659 So here we look at the root path of each subtree added during the
9660 merge and set explicit mergeinfo on it if it meets the aforementioned
9661 conditions. */
9662 if (range.start < range.end) /* Nothing to record on added subtrees
9663 resulting from reverse merges. */
9664 {
9665 SVN_ERR(record_mergeinfo_for_added_subtrees(
9666 &range, mergeinfo_path, depth,
9667 squelch_mergeinfo_notifications,
9668 merge_b->added_abspaths, merge_b, scratch_pool));
9669 }
9670 }
9671
9672 return SVN_NO_ERROR;
9673 }
9674
9675 /* Helper for do_merge() when the merge target is a directory.
9676 *
9677 * If any conflict is raised during the merge, set *CONFLICTED_RANGE to
9678 * the revision sub-range that raised the conflict. In this case, the
9679 * merge will have ended at revision CONFLICTED_RANGE and mergeinfo will
9680 * have been recorded for all revision sub-ranges up to and including
9681 * CONFLICTED_RANGE. Otherwise, set *CONFLICTED_RANGE to NULL.
9682 */
9683 static svn_error_t *
do_directory_merge(svn_mergeinfo_catalog_t result_catalog,single_range_conflict_report_t ** conflict_report,const merge_source_t * source,const char * target_abspath,const svn_diff_tree_processor_t * processor,svn_depth_t depth,svn_boolean_t squelch_mergeinfo_notifications,merge_cmd_baton_t * merge_b,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9684 do_directory_merge(svn_mergeinfo_catalog_t result_catalog,
9685 single_range_conflict_report_t **conflict_report,
9686 const merge_source_t *source,
9687 const char *target_abspath,
9688 const svn_diff_tree_processor_t *processor,
9689 svn_depth_t depth,
9690 svn_boolean_t squelch_mergeinfo_notifications,
9691 merge_cmd_baton_t *merge_b,
9692 apr_pool_t *result_pool,
9693 apr_pool_t *scratch_pool)
9694 {
9695 apr_array_header_t *children_with_mergeinfo;
9696
9697 /* Initialize CHILDREN_WITH_MERGEINFO. See the comment
9698 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */
9699 children_with_mergeinfo =
9700 apr_array_make(scratch_pool, 16, sizeof(svn_client__merge_path_t *));
9701
9702 /* And make it read-only accessible from the baton */
9703 merge_b->children_with_mergeinfo = children_with_mergeinfo;
9704
9705 /* If we are not honoring mergeinfo we can skip right to the
9706 business of merging changes! */
9707 if (HONOR_MERGEINFO(merge_b))
9708 SVN_ERR(do_mergeinfo_aware_dir_merge(result_catalog, conflict_report,
9709 source, target_abspath,
9710 children_with_mergeinfo,
9711 processor, depth,
9712 squelch_mergeinfo_notifications,
9713 merge_b, result_pool, scratch_pool));
9714 else
9715 SVN_ERR(do_mergeinfo_unaware_dir_merge(conflict_report,
9716 source, target_abspath,
9717 children_with_mergeinfo,
9718 processor, depth,
9719 merge_b, result_pool, scratch_pool));
9720
9721 merge_b->children_with_mergeinfo = NULL;
9722
9723 return SVN_NO_ERROR;
9724 }
9725
9726 /** Ensure that *RA_SESSION is opened to URL, either by reusing
9727 * *RA_SESSION if it is non-null and already opened to URL's
9728 * repository, or by allocating a new *RA_SESSION in POOL.
9729 * (RA_SESSION itself cannot be null, of course.)
9730 *
9731 * CTX is used as for svn_client_open_ra_session().
9732 */
9733 static svn_error_t *
ensure_ra_session_url(svn_ra_session_t ** ra_session,const char * url,const char * wri_abspath,svn_client_ctx_t * ctx,apr_pool_t * pool)9734 ensure_ra_session_url(svn_ra_session_t **ra_session,
9735 const char *url,
9736 const char *wri_abspath,
9737 svn_client_ctx_t *ctx,
9738 apr_pool_t *pool)
9739 {
9740 svn_error_t *err = SVN_NO_ERROR;
9741
9742 if (*ra_session)
9743 {
9744 err = svn_ra_reparent(*ra_session, url, pool);
9745 }
9746
9747 /* SVN_ERR_RA_ILLEGAL_URL is raised when url doesn't point to the same
9748 repository as ra_session. */
9749 if (! *ra_session || (err && err->apr_err == SVN_ERR_RA_ILLEGAL_URL))
9750 {
9751 svn_error_clear(err);
9752 err = svn_client_open_ra_session2(ra_session, url, wri_abspath,
9753 ctx, pool, pool);
9754 }
9755 SVN_ERR(err);
9756
9757 return SVN_NO_ERROR;
9758 }
9759
9760 /* Drive a merge of MERGE_SOURCES into working copy node TARGET
9761 and possibly record mergeinfo describing the merge -- see
9762 RECORD_MERGEINFO().
9763
9764 If MODIFIED_SUBTREES is not NULL and all the MERGE_SOURCES are 'ancestral'
9765 or REINTEGRATE_MERGE is true, then replace *MODIFIED_SUBTREES with a new
9766 hash containing all the paths that *MODIFIED_SUBTREES contained before,
9767 and also every path modified, skipped, added, or tree-conflicted
9768 by the merge. Keys and values of the hash are both (const char *)
9769 absolute paths. The contents of the hash are allocated in RESULT_POOL.
9770
9771 If the merge raises any conflicts while merging a revision range, return
9772 early and set *CONFLICT_REPORT to describe the details. (In this case,
9773 notify that the merge is complete if and only if this was the last
9774 revision range of the merge.) If there are no conflicts, set
9775 *CONFLICT_REPORT to NULL. A revision range here can be one specified
9776 in MERGE_SOURCES or an internally generated sub-range of one of those
9777 when merge tracking is in use.
9778
9779 For every (const merge_source_t *) merge source in MERGE_SOURCES, if
9780 SOURCE->ANCESTRAL is set, then the "left" and "right" side are
9781 ancestrally related. (See 'MERGEINFO MERGE SOURCE NORMALIZATION'
9782 for more on what that means and how it matters.)
9783
9784 If SOURCES_RELATED is set, the "left" and "right" sides of the
9785 merge source are historically related (ancestors, uncles, second
9786 cousins thrice removed, etc...). (This is passed through to
9787 do_file_merge() to simulate the history checks that the repository
9788 logic does in the directory case.)
9789
9790 SAME_REPOS is TRUE iff the merge sources live in the same
9791 repository as the one from which the target working copy has been
9792 checked out.
9793
9794 If mergeinfo is being recorded, SQUELCH_MERGEINFO_NOTIFICATIONS is FALSE,
9795 and CTX->NOTIFY_FUNC2 is not NULL, then call CTX->NOTIFY_FUNC2 with
9796 CTX->NOTIFY_BATON2 and a svn_wc_notify_merge_record_info_begin
9797 notification before any mergeinfo changes are made to describe the merge
9798 performed.
9799
9800 If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
9801 is not NULL, then don't record the new mergeinfo on the WC, but instead
9802 record it in RESULT_CATALOG, where the keys are absolute working copy
9803 paths and the values are the new mergeinfos for each. Allocate additions
9804 to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
9805
9806 FORCE_DELETE, DRY_RUN, RECORD_ONLY, DEPTH, MERGE_OPTIONS,
9807 and CTX are as described in the docstring for svn_client_merge_peg3().
9808
9809 If IGNORE_MERGEINFO is true, disable merge tracking, by treating the two
9810 sources as unrelated even if they actually have a common ancestor. See
9811 the macro HONOR_MERGEINFO().
9812
9813 If DIFF_IGNORE_ANCESTRY is true, diff the 'left' and 'right' versions
9814 of a node (if they are the same kind) as if they were related, even if
9815 they are not related. Otherwise, diff unrelated items as a deletion
9816 of one thing and the addition of another.
9817
9818 If not NULL, RECORD_ONLY_PATHS is a hash of (const char *) paths mapped
9819 to the same. If RECORD_ONLY is true and RECORD_ONLY_PATHS is not NULL,
9820 then record mergeinfo describing the merge only on subtrees which contain
9821 items from RECORD_ONLY_PATHS. If RECORD_ONLY is true and RECORD_ONLY_PATHS
9822 is NULL, then record mergeinfo on every subtree with mergeinfo in
9823 TARGET.
9824
9825 REINTEGRATE_MERGE is TRUE if this is a reintegrate merge.
9826
9827 *USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
9828 integrity, *USE_SLEEP will be unchanged if no sleep is required.
9829
9830 SCRATCH_POOL is used for all temporary allocations.
9831 */
9832 static svn_error_t *
do_merge(apr_hash_t ** modified_subtrees,svn_mergeinfo_catalog_t result_catalog,svn_client__conflict_report_t ** conflict_report,svn_boolean_t * use_sleep,const apr_array_header_t * merge_sources,const merge_target_t * target,svn_ra_session_t * src_session,svn_boolean_t sources_related,svn_boolean_t same_repos,svn_boolean_t ignore_mergeinfo,svn_boolean_t diff_ignore_ancestry,svn_boolean_t force_delete,svn_boolean_t dry_run,svn_boolean_t record_only,apr_hash_t * record_only_paths,svn_boolean_t reintegrate_merge,svn_boolean_t squelch_mergeinfo_notifications,svn_depth_t depth,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9833 do_merge(apr_hash_t **modified_subtrees,
9834 svn_mergeinfo_catalog_t result_catalog,
9835 svn_client__conflict_report_t **conflict_report,
9836 svn_boolean_t *use_sleep,
9837 const apr_array_header_t *merge_sources,
9838 const merge_target_t *target,
9839 svn_ra_session_t *src_session,
9840 svn_boolean_t sources_related,
9841 svn_boolean_t same_repos,
9842 svn_boolean_t ignore_mergeinfo,
9843 svn_boolean_t diff_ignore_ancestry,
9844 svn_boolean_t force_delete,
9845 svn_boolean_t dry_run,
9846 svn_boolean_t record_only,
9847 apr_hash_t *record_only_paths,
9848 svn_boolean_t reintegrate_merge,
9849 svn_boolean_t squelch_mergeinfo_notifications,
9850 svn_depth_t depth,
9851 const apr_array_header_t *merge_options,
9852 svn_client_ctx_t *ctx,
9853 apr_pool_t *result_pool,
9854 apr_pool_t *scratch_pool)
9855 {
9856 merge_cmd_baton_t merge_cmd_baton = { 0 };
9857 svn_config_t *cfg;
9858 const char *diff3_cmd;
9859 const char *preserved_exts_str;
9860 int i;
9861 svn_boolean_t checked_mergeinfo_capability = FALSE;
9862 svn_ra_session_t *ra_session1 = NULL, *ra_session2 = NULL;
9863 const char *old_src_session_url = NULL;
9864 apr_pool_t *iterpool;
9865 const svn_diff_tree_processor_t *processor;
9866
9867 SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath));
9868
9869 *conflict_report = NULL;
9870
9871 /* Check from some special conditions when in record-only mode
9872 (which is a merge-tracking thing). */
9873 if (record_only)
9874 {
9875 svn_boolean_t sources_ancestral = TRUE;
9876 int j;
9877
9878 /* Find out whether all of the sources are 'ancestral'. */
9879 for (j = 0; j < merge_sources->nelts; j++)
9880 if (! APR_ARRAY_IDX(merge_sources, j, merge_source_t *)->ancestral)
9881 {
9882 sources_ancestral = FALSE;
9883 break;
9884 }
9885
9886 /* We can't do a record-only merge if the sources aren't related. */
9887 if (! sources_ancestral)
9888 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
9889 _("Use of two URLs is not compatible with "
9890 "mergeinfo modification"));
9891
9892 /* We can't do a record-only merge if the sources aren't from
9893 the same repository as the target. */
9894 if (! same_repos)
9895 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
9896 _("Merge from foreign repository is not "
9897 "compatible with mergeinfo modification"));
9898
9899 /* If this is a dry-run record-only merge, there's nothing to do. */
9900 if (dry_run)
9901 return SVN_NO_ERROR;
9902 }
9903
9904 iterpool = svn_pool_create(scratch_pool);
9905
9906 /* Ensure a known depth. */
9907 if (depth == svn_depth_unknown)
9908 depth = svn_depth_infinity;
9909
9910 /* Set up the diff3 command, so various callers don't have to. */
9911 cfg = ctx->config
9912 ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG)
9913 : NULL;
9914 svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS,
9915 SVN_CONFIG_OPTION_DIFF3_CMD, NULL);
9916
9917 if (diff3_cmd != NULL)
9918 SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool));
9919
9920 /* See which files the user wants to preserve the extension of when
9921 conflict files are made. */
9922 svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY,
9923 SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, "");
9924
9925 /* Build the merge context baton (or at least the parts of it that
9926 don't need to be reset for each merge source). */
9927 merge_cmd_baton.force_delete = force_delete;
9928 merge_cmd_baton.dry_run = dry_run;
9929 merge_cmd_baton.record_only = record_only;
9930 merge_cmd_baton.ignore_mergeinfo = ignore_mergeinfo;
9931 merge_cmd_baton.diff_ignore_ancestry = diff_ignore_ancestry;
9932 merge_cmd_baton.same_repos = same_repos;
9933 merge_cmd_baton.mergeinfo_capable = FALSE;
9934 merge_cmd_baton.ctx = ctx;
9935 merge_cmd_baton.reintegrate_merge = reintegrate_merge;
9936 merge_cmd_baton.target = target;
9937 merge_cmd_baton.pool = iterpool;
9938 merge_cmd_baton.merge_options = merge_options;
9939 merge_cmd_baton.diff3_cmd = diff3_cmd;
9940 merge_cmd_baton.ext_patterns = *preserved_exts_str
9941 ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ",
9942 FALSE, scratch_pool)
9943 : NULL;
9944
9945 merge_cmd_baton.use_sleep = use_sleep;
9946
9947 /* Do we already know the specific subtrees with mergeinfo we want
9948 to record-only mergeinfo on? */
9949 if (record_only && record_only_paths)
9950 merge_cmd_baton.merged_abspaths = record_only_paths;
9951 else
9952 merge_cmd_baton.merged_abspaths = apr_hash_make(result_pool);
9953
9954 merge_cmd_baton.skipped_abspaths = apr_hash_make(result_pool);
9955 merge_cmd_baton.added_abspaths = apr_hash_make(result_pool);
9956 merge_cmd_baton.tree_conflicted_abspaths = apr_hash_make(result_pool);
9957
9958 merge_cmd_baton.notify_func = notify_merging;
9959 merge_cmd_baton.notify_baton = &merge_cmd_baton.notify_begin;
9960 merge_cmd_baton.notify_begin.merge_b = &merge_cmd_baton;
9961 merge_cmd_baton.notify_begin.notify_func2 = ctx->notify_func2;
9962 merge_cmd_baton.notify_begin.notify_baton2 = ctx->notify_baton2;
9963
9964 processor = merge_apply_processor(&merge_cmd_baton, scratch_pool);
9965
9966 if (src_session)
9967 {
9968 SVN_ERR(svn_ra_get_session_url(src_session, &old_src_session_url,
9969 scratch_pool));
9970 ra_session1 = src_session;
9971 }
9972
9973 for (i = 0; i < merge_sources->nelts; i++)
9974 {
9975 svn_node_kind_t src1_kind;
9976 merge_source_t *source =
9977 APR_ARRAY_IDX(merge_sources, i, merge_source_t *);
9978 single_range_conflict_report_t *conflicted_range_report;
9979
9980 svn_pool_clear(iterpool);
9981
9982 /* Sanity check: if our left- and right-side merge sources are
9983 the same, there's nothing to here. */
9984 if ((strcmp(source->loc1->url, source->loc2->url) == 0)
9985 && (source->loc1->rev == source->loc2->rev))
9986 continue;
9987
9988 /* Establish RA sessions to our URLs, reuse where possible. */
9989 SVN_ERR(ensure_ra_session_url(&ra_session1, source->loc1->url,
9990 target->abspath, ctx, scratch_pool));
9991 SVN_ERR(ensure_ra_session_url(&ra_session2, source->loc2->url,
9992 target->abspath, ctx, scratch_pool));
9993
9994 /* Populate the portions of the merge context baton that need to
9995 be reset for each merge source iteration. */
9996 merge_cmd_baton.merge_source = *source;
9997 merge_cmd_baton.implicit_src_gap = NULL;
9998 merge_cmd_baton.conflicted_paths = NULL;
9999 merge_cmd_baton.paths_with_new_mergeinfo = NULL;
10000 merge_cmd_baton.paths_with_deleted_mergeinfo = NULL;
10001 merge_cmd_baton.ra_session1 = ra_session1;
10002 merge_cmd_baton.ra_session2 = ra_session2;
10003
10004 merge_cmd_baton.notify_begin.last_abspath = NULL;
10005
10006 /* Populate the portions of the merge context baton that require
10007 an RA session to set, but shouldn't be reset for each iteration. */
10008 if (! checked_mergeinfo_capability)
10009 {
10010 SVN_ERR(svn_ra_has_capability(ra_session1,
10011 &merge_cmd_baton.mergeinfo_capable,
10012 SVN_RA_CAPABILITY_MERGEINFO,
10013 iterpool));
10014 checked_mergeinfo_capability = TRUE;
10015 }
10016
10017 SVN_ERR(svn_ra_check_path(ra_session1, "", source->loc1->rev,
10018 &src1_kind, iterpool));
10019
10020 /* Run the merge; if there are conflicts, allow the callback to
10021 * resolve them, and if it resolves all of them, then run the
10022 * merge again with the remaining revision range, until it is all
10023 * done. */
10024 do
10025 {
10026 /* Merge as far as possible without resolving any conflicts */
10027 if (src1_kind != svn_node_dir)
10028 {
10029 SVN_ERR(do_file_merge(result_catalog, &conflicted_range_report,
10030 source, target->abspath,
10031 processor,
10032 sources_related,
10033 squelch_mergeinfo_notifications,
10034 &merge_cmd_baton, iterpool, iterpool));
10035 }
10036 else /* Directory */
10037 {
10038 SVN_ERR(do_directory_merge(result_catalog, &conflicted_range_report,
10039 source, target->abspath,
10040 processor,
10041 depth, squelch_mergeinfo_notifications,
10042 &merge_cmd_baton, iterpool, iterpool));
10043 }
10044
10045 /* Give the conflict resolver callback the opportunity to
10046 * resolve any conflicts that were raised. If it resolves all
10047 * of them, go around again to merge the next sub-range (if any). */
10048 if (conflicted_range_report && ctx->conflict_func2 && ! dry_run)
10049 {
10050 svn_boolean_t conflicts_remain;
10051
10052 SVN_ERR(svn_client__resolve_conflicts(
10053 &conflicts_remain, merge_cmd_baton.conflicted_paths,
10054 ctx, iterpool));
10055 if (conflicts_remain)
10056 break;
10057
10058 merge_cmd_baton.conflicted_paths = NULL;
10059 /* Caution: this source is in iterpool */
10060 source = conflicted_range_report->remaining_source;
10061 conflicted_range_report = NULL;
10062 }
10063 else
10064 break;
10065 }
10066 while (source);
10067
10068 /* The final mergeinfo on TARGET_WCPATH may itself elide. */
10069 if (! dry_run)
10070 SVN_ERR(svn_client__elide_mergeinfo(target->abspath, NULL,
10071 ctx, iterpool));
10072
10073 /* If conflicts occurred while merging any but the very last
10074 * range of a multi-pass merge, we raise an error that aborts
10075 * the merge. The user will be asked to resolve conflicts
10076 * before merging subsequent revision ranges. */
10077 if (conflicted_range_report)
10078 {
10079 *conflict_report = conflict_report_create(
10080 target->abspath, conflicted_range_report->conflicted_range,
10081 (i == merge_sources->nelts - 1
10082 && ! conflicted_range_report->remaining_source),
10083 result_pool);
10084 break;
10085 }
10086 }
10087
10088 if (! *conflict_report || (*conflict_report)->was_last_range)
10089 {
10090 /* Let everyone know we're finished here. */
10091 notify_merge_completed(target->abspath, ctx, iterpool);
10092 }
10093
10094 /* Does the caller want to know what the merge has done? */
10095 if (modified_subtrees)
10096 {
10097 *modified_subtrees =
10098 apr_hash_overlay(result_pool, *modified_subtrees,
10099 merge_cmd_baton.merged_abspaths);
10100 *modified_subtrees =
10101 apr_hash_overlay(result_pool, *modified_subtrees,
10102 merge_cmd_baton.added_abspaths);
10103 *modified_subtrees =
10104 apr_hash_overlay(result_pool, *modified_subtrees,
10105 merge_cmd_baton.skipped_abspaths);
10106 *modified_subtrees =
10107 apr_hash_overlay(result_pool, *modified_subtrees,
10108 merge_cmd_baton.tree_conflicted_abspaths);
10109 }
10110
10111 if (src_session)
10112 SVN_ERR(svn_ra_reparent(src_session, old_src_session_url, iterpool));
10113
10114 svn_pool_destroy(iterpool);
10115 return SVN_NO_ERROR;
10116 }
10117
10118 /* Perform a two-URL merge between URLs which are related, but neither
10119 is a direct ancestor of the other. This first does a real two-URL
10120 merge (unless this is record-only), followed by record-only merges
10121 to represent the changed mergeinfo.
10122
10123 Set *CONFLICT_REPORT to indicate if there were any conflicts, as in
10124 do_merge().
10125
10126 The diff to be merged is between SOURCE->loc1 (in URL1_RA_SESSION1)
10127 and SOURCE->loc2 (in URL2_RA_SESSION2); YCA is their youngest
10128 common ancestor.
10129
10130 SAME_REPOS must be true if and only if the source URLs are in the same
10131 repository as the target working copy.
10132
10133 DIFF_IGNORE_ANCESTRY is as in do_merge().
10134
10135 Other arguments are as in all of the public merge APIs.
10136
10137 *USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
10138 integrity, *USE_SLEEP will be unchanged if no sleep is required.
10139
10140 SCRATCH_POOL is used for all temporary allocations.
10141 */
10142 static svn_error_t *
merge_cousins_and_supplement_mergeinfo(svn_client__conflict_report_t ** conflict_report,svn_boolean_t * use_sleep,const merge_target_t * target,svn_ra_session_t * URL1_ra_session,svn_ra_session_t * URL2_ra_session,const merge_source_t * source,const svn_client__pathrev_t * yca,svn_boolean_t same_repos,svn_depth_t depth,svn_boolean_t diff_ignore_ancestry,svn_boolean_t force_delete,svn_boolean_t record_only,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)10143 merge_cousins_and_supplement_mergeinfo(
10144 svn_client__conflict_report_t **conflict_report,
10145 svn_boolean_t *use_sleep,
10146 const merge_target_t *target,
10147 svn_ra_session_t *URL1_ra_session,
10148 svn_ra_session_t *URL2_ra_session,
10149 const merge_source_t *source,
10150 const svn_client__pathrev_t *yca,
10151 svn_boolean_t same_repos,
10152 svn_depth_t depth,
10153 svn_boolean_t diff_ignore_ancestry,
10154 svn_boolean_t force_delete,
10155 svn_boolean_t record_only,
10156 svn_boolean_t dry_run,
10157 const apr_array_header_t *merge_options,
10158 svn_client_ctx_t *ctx,
10159 apr_pool_t *result_pool,
10160 apr_pool_t *scratch_pool)
10161 {
10162 apr_array_header_t *remove_sources, *add_sources;
10163 apr_hash_t *modified_subtrees = NULL;
10164
10165 /* Sure we could use SCRATCH_POOL throughout this function, but since this
10166 is a wrapper around three separate merges we'll create a subpool we can
10167 clear between each of the three. If the merge target has a lot of
10168 subtree mergeinfo, then this will help keep memory use in check. */
10169 apr_pool_t *subpool = svn_pool_create(scratch_pool);
10170
10171 assert(session_url_is(URL1_ra_session, source->loc1->url, scratch_pool));
10172 assert(session_url_is(URL2_ra_session, source->loc2->url, scratch_pool));
10173
10174 SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath));
10175 SVN_ERR_ASSERT(! source->ancestral);
10176
10177 SVN_ERR(normalize_merge_sources_internal(
10178 &remove_sources, source->loc1,
10179 svn_rangelist__initialize(source->loc1->rev, yca->rev, TRUE,
10180 scratch_pool),
10181 URL1_ra_session, ctx, scratch_pool, subpool));
10182
10183 SVN_ERR(normalize_merge_sources_internal(
10184 &add_sources, source->loc2,
10185 svn_rangelist__initialize(yca->rev, source->loc2->rev, TRUE,
10186 scratch_pool),
10187 URL2_ra_session, ctx, scratch_pool, subpool));
10188
10189 *conflict_report = NULL;
10190
10191 /* If this isn't a record-only merge, we'll first do a stupid
10192 point-to-point merge... */
10193 if (! record_only)
10194 {
10195 apr_array_header_t *faux_sources =
10196 apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
10197
10198 modified_subtrees = apr_hash_make(scratch_pool);
10199 APR_ARRAY_PUSH(faux_sources, const merge_source_t *) = source;
10200 SVN_ERR(do_merge(&modified_subtrees, NULL, conflict_report, use_sleep,
10201 faux_sources, target,
10202 URL1_ra_session, TRUE, same_repos,
10203 FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10204 force_delete, dry_run, FALSE, NULL, TRUE,
10205 FALSE, depth, merge_options, ctx,
10206 scratch_pool, subpool));
10207 if (*conflict_report)
10208 {
10209 *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10210 if (! (*conflict_report)->was_last_range)
10211 return SVN_NO_ERROR;
10212 }
10213 }
10214 else if (! same_repos)
10215 {
10216 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
10217 _("Merge from foreign repository is not "
10218 "compatible with mergeinfo modification"));
10219 }
10220
10221 /* ... and now, if we're doing the mergeinfo thang, we execute a
10222 pair of record-only merges using the real sources we've
10223 calculated.
10224
10225 Issue #3648: We don't actually perform these two record-only merges
10226 on the WC at first, but rather see what each would do and store that
10227 in two mergeinfo catalogs. We then merge the catalogs together and
10228 then record the result in the WC. This prevents the second record
10229 only merge from removing legitimate mergeinfo history, from the same
10230 source, that was made in prior merges. */
10231 if (same_repos && !dry_run)
10232 {
10233 svn_mergeinfo_catalog_t add_result_catalog =
10234 apr_hash_make(scratch_pool);
10235 svn_mergeinfo_catalog_t remove_result_catalog =
10236 apr_hash_make(scratch_pool);
10237
10238 notify_mergeinfo_recording(target->abspath, NULL, ctx, scratch_pool);
10239 svn_pool_clear(subpool);
10240 SVN_ERR(do_merge(NULL, add_result_catalog, conflict_report, use_sleep,
10241 add_sources, target,
10242 URL1_ra_session, TRUE, same_repos,
10243 FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10244 force_delete, dry_run, TRUE,
10245 modified_subtrees, TRUE,
10246 TRUE, depth, merge_options, ctx,
10247 scratch_pool, subpool));
10248 if (*conflict_report)
10249 {
10250 *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10251 if (! (*conflict_report)->was_last_range)
10252 return SVN_NO_ERROR;
10253 }
10254 svn_pool_clear(subpool);
10255 SVN_ERR(do_merge(NULL, remove_result_catalog, conflict_report, use_sleep,
10256 remove_sources, target,
10257 URL1_ra_session, TRUE, same_repos,
10258 FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10259 force_delete, dry_run, TRUE,
10260 modified_subtrees, TRUE,
10261 TRUE, depth, merge_options, ctx,
10262 scratch_pool, subpool));
10263 if (*conflict_report)
10264 {
10265 *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10266 if (! (*conflict_report)->was_last_range)
10267 return SVN_NO_ERROR;
10268 }
10269 SVN_ERR(svn_mergeinfo_catalog_merge(add_result_catalog,
10270 remove_result_catalog,
10271 scratch_pool, scratch_pool));
10272 SVN_ERR(svn_client__record_wc_mergeinfo_catalog(add_result_catalog,
10273 ctx, scratch_pool));
10274 }
10275
10276 svn_pool_destroy(subpool);
10277 return SVN_NO_ERROR;
10278 }
10279
10280 /* Perform checks to determine whether the working copy at TARGET_ABSPATH
10281 * can safely be used as a merge target. Checks are performed according to
10282 * the ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, and ALLOW_SWITCHED_SUBTREES
10283 * parameters. If any checks fail, raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE.
10284 *
10285 * E.g. if all the ALLOW_* parameters are FALSE, TARGET_ABSPATH must
10286 * be a single-revision, pristine, unswitched working copy.
10287 * In other words, it must reflect a subtree of the repository as found
10288 * at single revision -- although sparse checkouts are permitted. */
10289 static svn_error_t *
ensure_wc_is_suitable_merge_target(const char * target_abspath,svn_client_ctx_t * ctx,svn_boolean_t allow_mixed_rev,svn_boolean_t allow_local_mods,svn_boolean_t allow_switched_subtrees,apr_pool_t * scratch_pool)10290 ensure_wc_is_suitable_merge_target(const char *target_abspath,
10291 svn_client_ctx_t *ctx,
10292 svn_boolean_t allow_mixed_rev,
10293 svn_boolean_t allow_local_mods,
10294 svn_boolean_t allow_switched_subtrees,
10295 apr_pool_t *scratch_pool)
10296 {
10297 svn_node_kind_t target_kind;
10298
10299 /* Check the target exists. */
10300 SVN_ERR(svn_io_check_path(target_abspath, &target_kind, scratch_pool));
10301 if (target_kind == svn_node_none)
10302 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
10303 _("Path '%s' does not exist"),
10304 svn_dirent_local_style(target_abspath,
10305 scratch_pool));
10306 SVN_ERR(svn_wc_read_kind2(&target_kind, ctx->wc_ctx, target_abspath,
10307 FALSE, FALSE, scratch_pool));
10308 if (target_kind != svn_node_dir && target_kind != svn_node_file)
10309 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
10310 _("Merge target '%s' does not exist in the "
10311 "working copy"), target_abspath);
10312
10313 /* Perform the mixed-revision check first because it's the cheapest one. */
10314 if (! allow_mixed_rev)
10315 {
10316 svn_revnum_t min_rev;
10317 svn_revnum_t max_rev;
10318
10319 SVN_ERR(svn_client_min_max_revisions(&min_rev, &max_rev, target_abspath,
10320 FALSE, ctx, scratch_pool));
10321
10322 if (!(SVN_IS_VALID_REVNUM(min_rev) && SVN_IS_VALID_REVNUM(max_rev)))
10323 {
10324 svn_boolean_t is_added;
10325
10326 /* Allow merge into added nodes. */
10327 SVN_ERR(svn_wc__node_is_added(&is_added, ctx->wc_ctx, target_abspath,
10328 scratch_pool));
10329 if (is_added)
10330 return SVN_NO_ERROR;
10331 else
10332 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10333 _("Cannot determine revision of working "
10334 "copy"));
10335 }
10336
10337 if (min_rev != max_rev)
10338 return svn_error_createf(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
10339 _("Cannot merge into mixed-revision working "
10340 "copy [%ld:%ld]; try updating first"),
10341 min_rev, max_rev);
10342 }
10343
10344 /* Next, check for switched subtrees. */
10345 if (! allow_switched_subtrees)
10346 {
10347 svn_boolean_t is_switched;
10348
10349 SVN_ERR(svn_wc__has_switched_subtrees(&is_switched, ctx->wc_ctx,
10350 target_abspath, NULL,
10351 scratch_pool));
10352 if (is_switched)
10353 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10354 _("Cannot merge into a working copy "
10355 "with a switched subtree"));
10356 }
10357
10358 /* This is the most expensive check, so it is performed last.*/
10359 if (! allow_local_mods)
10360 {
10361 svn_boolean_t is_modified;
10362
10363 SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx,
10364 target_abspath, TRUE,
10365 ctx->cancel_func,
10366 ctx->cancel_baton,
10367 scratch_pool));
10368 if (is_modified)
10369 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10370 _("Cannot merge into a working copy "
10371 "that has local modifications"));
10372 }
10373
10374 return SVN_NO_ERROR;
10375 }
10376
10377 /* Throw an error if PATH_OR_URL is a path and REVISION isn't a repository
10378 * revision. */
10379 static svn_error_t *
ensure_wc_path_has_repo_revision(const char * path_or_url,const svn_opt_revision_t * revision,apr_pool_t * scratch_pool)10380 ensure_wc_path_has_repo_revision(const char *path_or_url,
10381 const svn_opt_revision_t *revision,
10382 apr_pool_t *scratch_pool)
10383 {
10384 if (revision->kind != svn_opt_revision_number
10385 && revision->kind != svn_opt_revision_date
10386 && revision->kind != svn_opt_revision_head
10387 && ! svn_path_is_url(path_or_url))
10388 return svn_error_createf(
10389 SVN_ERR_CLIENT_BAD_REVISION, NULL,
10390 _("Invalid merge source '%s'; a working copy path can only be "
10391 "used with a repository revision (a number, a date, or head)"),
10392 svn_dirent_local_style(path_or_url, scratch_pool));
10393 return SVN_NO_ERROR;
10394 }
10395
10396 /* "Open" the target WC for a merge. That means:
10397 * - find out its exact repository location
10398 * - check the WC for suitability (throw an error if unsuitable)
10399 *
10400 * Set *TARGET_P to a new, fully initialized, target description structure.
10401 *
10402 * ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, ALLOW_SWITCHED_SUBTREES determine
10403 * whether the WC is deemed suitable; see ensure_wc_is_suitable_merge_target()
10404 * for details.
10405 *
10406 * If the node is locally added, the rev and URL will be null/invalid. Some
10407 * kinds of merge can use such a target; others can't.
10408 */
10409 static svn_error_t *
open_target_wc(merge_target_t ** target_p,const char * wc_abspath,svn_boolean_t allow_mixed_rev,svn_boolean_t allow_local_mods,svn_boolean_t allow_switched_subtrees,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10410 open_target_wc(merge_target_t **target_p,
10411 const char *wc_abspath,
10412 svn_boolean_t allow_mixed_rev,
10413 svn_boolean_t allow_local_mods,
10414 svn_boolean_t allow_switched_subtrees,
10415 svn_client_ctx_t *ctx,
10416 apr_pool_t *result_pool,
10417 apr_pool_t *scratch_pool)
10418 {
10419 merge_target_t *target = apr_palloc(result_pool, sizeof(*target));
10420 svn_client__pathrev_t *origin;
10421
10422 target->abspath = apr_pstrdup(result_pool, wc_abspath);
10423
10424 SVN_ERR(svn_client__wc_node_get_origin(&origin, wc_abspath, ctx,
10425 result_pool, scratch_pool));
10426 if (origin)
10427 {
10428 target->loc = *origin;
10429 }
10430 else
10431 {
10432 svn_error_t *err;
10433 /* The node has no location in the repository. It's unversioned or
10434 * locally added or locally deleted.
10435 *
10436 * If it's locally added or deleted, find the repository root
10437 * URL and UUID anyway, and leave the node URL and revision as NULL
10438 * and INVALID. If it's unversioned, this will throw an error. */
10439 err = svn_wc__node_get_repos_info(NULL, NULL,
10440 &target->loc.repos_root_url,
10441 &target->loc.repos_uuid,
10442 ctx->wc_ctx, wc_abspath,
10443 result_pool, scratch_pool);
10444
10445 if (err)
10446 {
10447 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
10448 && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY
10449 && err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
10450 return svn_error_trace(err);
10451
10452 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, err,
10453 _("Merge target '%s' does not exist in the "
10454 "working copy"),
10455 svn_dirent_local_style(wc_abspath,
10456 scratch_pool));
10457 }
10458
10459 target->loc.rev = SVN_INVALID_REVNUM;
10460 target->loc.url = NULL;
10461 }
10462
10463 SVN_ERR(ensure_wc_is_suitable_merge_target(
10464 wc_abspath, ctx,
10465 allow_mixed_rev, allow_local_mods, allow_switched_subtrees,
10466 scratch_pool));
10467
10468 *target_p = target;
10469 return SVN_NO_ERROR;
10470 }
10471
10472 /*-----------------------------------------------------------------------*/
10473
10474 /*** Public APIs ***/
10475
10476 /* The body of svn_client_merge5(), which see for details.
10477 *
10478 * If SOURCE1 @ REVISION1 is related to SOURCE2 @ REVISION2 then use merge
10479 * tracking (subject to other constraints -- see HONOR_MERGEINFO());
10480 * otherwise disable merge tracking.
10481 *
10482 * IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge().
10483 */
10484 svn_error_t *
svn_client__merge_locked(svn_client__conflict_report_t ** conflict_report,const char * source1,const svn_opt_revision_t * revision1,const char * source2,const svn_opt_revision_t * revision2,const char * target_abspath,svn_depth_t depth,svn_boolean_t ignore_mergeinfo,svn_boolean_t diff_ignore_ancestry,svn_boolean_t force_delete,svn_boolean_t record_only,svn_boolean_t dry_run,svn_boolean_t allow_mixed_rev,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10485 svn_client__merge_locked(svn_client__conflict_report_t **conflict_report,
10486 const char *source1,
10487 const svn_opt_revision_t *revision1,
10488 const char *source2,
10489 const svn_opt_revision_t *revision2,
10490 const char *target_abspath,
10491 svn_depth_t depth,
10492 svn_boolean_t ignore_mergeinfo,
10493 svn_boolean_t diff_ignore_ancestry,
10494 svn_boolean_t force_delete,
10495 svn_boolean_t record_only,
10496 svn_boolean_t dry_run,
10497 svn_boolean_t allow_mixed_rev,
10498 const apr_array_header_t *merge_options,
10499 svn_client_ctx_t *ctx,
10500 apr_pool_t *result_pool,
10501 apr_pool_t *scratch_pool)
10502 {
10503 merge_target_t *target;
10504 svn_client__pathrev_t *source1_loc, *source2_loc;
10505 svn_boolean_t sources_related = FALSE;
10506 svn_ra_session_t *ra_session1, *ra_session2;
10507 apr_array_header_t *merge_sources;
10508 svn_error_t *err;
10509 svn_boolean_t use_sleep = FALSE;
10510 svn_client__pathrev_t *yca = NULL;
10511 apr_pool_t *sesspool;
10512 svn_boolean_t same_repos;
10513
10514 /* ### FIXME: This function really ought to do a history check on
10515 the left and right sides of the merge source, and -- if one is an
10516 ancestor of the other -- just call svn_client_merge_peg3() with
10517 the appropriate args. */
10518
10519 SVN_ERR(open_target_wc(&target, target_abspath,
10520 allow_mixed_rev, TRUE, TRUE,
10521 ctx, scratch_pool, scratch_pool));
10522
10523 /* Open RA sessions to both sides of our merge source, and resolve URLs
10524 * and revisions. */
10525 sesspool = svn_pool_create(scratch_pool);
10526 SVN_ERR(svn_client__ra_session_from_path2(
10527 &ra_session1, &source1_loc,
10528 source1, NULL, revision1, revision1, ctx, sesspool));
10529 SVN_ERR(svn_client__ra_session_from_path2(
10530 &ra_session2, &source2_loc,
10531 source2, NULL, revision2, revision2, ctx, sesspool));
10532
10533 /* We can't do a diff between different repositories. */
10534 /* ### We should also insist that the root URLs of the two sources match,
10535 * as we are only carrying around a single source-repos-root from now
10536 * on, and URL calculations will go wrong if they differ.
10537 * Alternatively, teach the code to cope with differing root URLs. */
10538 SVN_ERR(check_same_repos(source1_loc, source1_loc->url,
10539 source2_loc, source2_loc->url,
10540 FALSE /* strict_urls */, scratch_pool));
10541
10542 /* Do our working copy and sources come from the same repository? */
10543 same_repos = is_same_repos(&target->loc, source1_loc, TRUE /* strict_urls */);
10544
10545 /* Unless we're ignoring ancestry, see if the two sources are related. */
10546 if (! ignore_mergeinfo)
10547 SVN_ERR(svn_client__get_youngest_common_ancestor(
10548 &yca, source1_loc, source2_loc, ra_session1, ctx,
10549 scratch_pool, scratch_pool));
10550
10551 /* Check for a youngest common ancestor. If we have one, we'll be
10552 doing merge tracking.
10553
10554 So, given a requested merge of the differences between A and
10555 B, and a common ancestor of C, we will find ourselves in one of
10556 four positions, and four different approaches:
10557
10558 A == B == C there's nothing to merge
10559
10560 A == C != B we merge the changes between A (or C) and B
10561
10562 B == C != A we merge the changes between B (or C) and A
10563
10564 A != B != C we merge the changes between A and B without
10565 merge recording, then record-only two merges:
10566 from A to C, and from C to B
10567 */
10568 if (yca)
10569 {
10570 /* Note that our merge sources are related. */
10571 sources_related = TRUE;
10572
10573 /* If the common ancestor matches the right side of our merge,
10574 then we only need to reverse-merge the left side. */
10575 if ((strcmp(yca->url, source2_loc->url) == 0)
10576 && (yca->rev == source2_loc->rev))
10577 {
10578 SVN_ERR(normalize_merge_sources_internal(
10579 &merge_sources, source1_loc,
10580 svn_rangelist__initialize(source1_loc->rev, yca->rev, TRUE,
10581 scratch_pool),
10582 ra_session1, ctx, scratch_pool, scratch_pool));
10583 }
10584 /* If the common ancestor matches the left side of our merge,
10585 then we only need to merge the right side. */
10586 else if ((strcmp(yca->url, source1_loc->url) == 0)
10587 && (yca->rev == source1_loc->rev))
10588 {
10589 SVN_ERR(normalize_merge_sources_internal(
10590 &merge_sources, source2_loc,
10591 svn_rangelist__initialize(yca->rev, source2_loc->rev, TRUE,
10592 scratch_pool),
10593 ra_session2, ctx, scratch_pool, scratch_pool));
10594 }
10595 /* And otherwise, we need to do both: reverse merge the left
10596 side, and merge the right. */
10597 else
10598 {
10599 merge_source_t source;
10600
10601 source.loc1 = source1_loc;
10602 source.loc2 = source2_loc;
10603 source.ancestral = FALSE;
10604
10605 err = merge_cousins_and_supplement_mergeinfo(conflict_report,
10606 &use_sleep,
10607 target,
10608 ra_session1,
10609 ra_session2,
10610 &source,
10611 yca,
10612 same_repos,
10613 depth,
10614 diff_ignore_ancestry,
10615 force_delete,
10616 record_only, dry_run,
10617 merge_options,
10618 ctx,
10619 result_pool,
10620 scratch_pool);
10621 /* Close our temporary RA sessions (this could've happened
10622 after the second call to normalize_merge_sources() inside
10623 the merge_cousins_and_supplement_mergeinfo() routine). */
10624 svn_pool_destroy(sesspool);
10625
10626 if (use_sleep)
10627 svn_io_sleep_for_timestamps(target->abspath, scratch_pool);
10628
10629 SVN_ERR(err);
10630 return SVN_NO_ERROR;
10631 }
10632 }
10633 else
10634 {
10635 /* Build a single-item merge_source_t array. */
10636 merge_sources = apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
10637 APR_ARRAY_PUSH(merge_sources, merge_source_t *)
10638 = merge_source_create(source1_loc, source2_loc, FALSE, scratch_pool);
10639 }
10640
10641 err = do_merge(NULL, NULL, conflict_report, &use_sleep,
10642 merge_sources, target,
10643 ra_session1, sources_related, same_repos,
10644 ignore_mergeinfo, diff_ignore_ancestry, force_delete, dry_run,
10645 record_only, NULL, FALSE, FALSE, depth, merge_options,
10646 ctx, result_pool, scratch_pool);
10647
10648 /* Close our temporary RA sessions. */
10649 svn_pool_destroy(sesspool);
10650
10651 if (use_sleep)
10652 svn_io_sleep_for_timestamps(target->abspath, scratch_pool);
10653
10654 SVN_ERR(err);
10655 return SVN_NO_ERROR;
10656 }
10657
10658 /* Set *TARGET_ABSPATH to the absolute path of, and *LOCK_ABSPATH to
10659 the absolute path to lock for, TARGET_WCPATH. */
10660 static svn_error_t *
get_target_and_lock_abspath(const char ** target_abspath,const char ** lock_abspath,const char * target_wcpath,svn_client_ctx_t * ctx,apr_pool_t * result_pool)10661 get_target_and_lock_abspath(const char **target_abspath,
10662 const char **lock_abspath,
10663 const char *target_wcpath,
10664 svn_client_ctx_t *ctx,
10665 apr_pool_t *result_pool)
10666 {
10667 svn_node_kind_t kind;
10668 SVN_ERR(svn_dirent_get_absolute(target_abspath, target_wcpath,
10669 result_pool));
10670 SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, *target_abspath,
10671 FALSE, FALSE, result_pool));
10672 if (kind == svn_node_dir)
10673 *lock_abspath = *target_abspath;
10674 else
10675 *lock_abspath = svn_dirent_dirname(*target_abspath, result_pool);
10676
10677 return SVN_NO_ERROR;
10678 }
10679
10680 svn_error_t *
svn_client_merge5(const char * source1,const svn_opt_revision_t * revision1,const char * source2,const svn_opt_revision_t * revision2,const char * target_wcpath,svn_depth_t depth,svn_boolean_t ignore_mergeinfo,svn_boolean_t diff_ignore_ancestry,svn_boolean_t force_delete,svn_boolean_t record_only,svn_boolean_t dry_run,svn_boolean_t allow_mixed_rev,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * pool)10681 svn_client_merge5(const char *source1,
10682 const svn_opt_revision_t *revision1,
10683 const char *source2,
10684 const svn_opt_revision_t *revision2,
10685 const char *target_wcpath,
10686 svn_depth_t depth,
10687 svn_boolean_t ignore_mergeinfo,
10688 svn_boolean_t diff_ignore_ancestry,
10689 svn_boolean_t force_delete,
10690 svn_boolean_t record_only,
10691 svn_boolean_t dry_run,
10692 svn_boolean_t allow_mixed_rev,
10693 const apr_array_header_t *merge_options,
10694 svn_client_ctx_t *ctx,
10695 apr_pool_t *pool)
10696 {
10697 const char *target_abspath, *lock_abspath;
10698 svn_client__conflict_report_t *conflict_report;
10699
10700 /* Sanity check our input -- we require specified revisions,
10701 * and either 2 paths or 2 URLs. */
10702 if ((revision1->kind == svn_opt_revision_unspecified)
10703 || (revision2->kind == svn_opt_revision_unspecified))
10704 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
10705 _("Not all required revisions are specified"));
10706 if (svn_path_is_url(source1) != svn_path_is_url(source2))
10707 return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
10708 _("Merge sources must both be "
10709 "either paths or URLs"));
10710 /* A WC path must be used with a repository revision, as we can't
10711 * (currently) use the WC itself as a source, we can only read the URL
10712 * from it and use that. */
10713 SVN_ERR(ensure_wc_path_has_repo_revision(source1, revision1, pool));
10714 SVN_ERR(ensure_wc_path_has_repo_revision(source2, revision2, pool));
10715
10716 SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
10717 target_wcpath, ctx, pool));
10718
10719 if (!dry_run)
10720 SVN_WC__CALL_WITH_WRITE_LOCK(
10721 svn_client__merge_locked(&conflict_report,
10722 source1, revision1, source2, revision2,
10723 target_abspath, depth, ignore_mergeinfo,
10724 diff_ignore_ancestry,
10725 force_delete, record_only, dry_run,
10726 allow_mixed_rev, merge_options, ctx, pool, pool),
10727 ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
10728 else
10729 SVN_ERR(svn_client__merge_locked(&conflict_report,
10730 source1, revision1, source2, revision2,
10731 target_abspath, depth, ignore_mergeinfo,
10732 diff_ignore_ancestry,
10733 force_delete, record_only, dry_run,
10734 allow_mixed_rev, merge_options, ctx, pool,
10735 pool));
10736
10737 SVN_ERR(svn_client__make_merge_conflict_error(conflict_report, pool));
10738 return SVN_NO_ERROR;
10739 }
10740
10741
10742 /* Check if mergeinfo for a given path is described explicitly or via
10743 inheritance in a mergeinfo catalog.
10744
10745 If REPOS_REL_PATH exists in CATALOG and has mergeinfo containing
10746 MERGEINFO, then set *IN_CATALOG to TRUE. If REPOS_REL_PATH does
10747 not exist in CATALOG, then find its nearest parent which does exist.
10748 If the mergeinfo REPOS_REL_PATH would inherit from that parent
10749 contains MERGEINFO then set *IN_CATALOG to TRUE. Set *IN_CATALOG
10750 to FALSE in all other cases.
10751
10752 Set *CAT_KEY_PATH to the key path in CATALOG for REPOS_REL_PATH's
10753 explicit or inherited mergeinfo. If no explicit or inherited mergeinfo
10754 is found for REPOS_REL_PATH then set *CAT_KEY_PATH to NULL.
10755
10756 User RESULT_POOL to allocate *CAT_KEY_PATH. Use SCRATCH_POOL for
10757 temporary allocations. */
10758 static svn_error_t *
mergeinfo_in_catalog(svn_boolean_t * in_catalog,const char ** cat_key_path,const char * repos_rel_path,svn_mergeinfo_t mergeinfo,svn_mergeinfo_catalog_t catalog,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10759 mergeinfo_in_catalog(svn_boolean_t *in_catalog,
10760 const char **cat_key_path,
10761 const char *repos_rel_path,
10762 svn_mergeinfo_t mergeinfo,
10763 svn_mergeinfo_catalog_t catalog,
10764 apr_pool_t *result_pool,
10765 apr_pool_t *scratch_pool)
10766 {
10767 const char *walk_path = NULL;
10768
10769 *in_catalog = FALSE;
10770 *cat_key_path = NULL;
10771
10772 if (mergeinfo && catalog && apr_hash_count(catalog))
10773 {
10774 const char *path = repos_rel_path;
10775
10776 /* Start with the assumption there is no explicit or inherited
10777 mergeinfo for REPOS_REL_PATH in CATALOG. */
10778 svn_mergeinfo_t mergeinfo_in_cat = NULL;
10779
10780 while (1)
10781 {
10782 mergeinfo_in_cat = svn_hash_gets(catalog, path);
10783
10784 if (mergeinfo_in_cat) /* Found it! */
10785 {
10786 *cat_key_path = apr_pstrdup(result_pool, path);
10787 break;
10788 }
10789 else /* Look for inherited mergeinfo. */
10790 {
10791 walk_path = svn_relpath_join(svn_relpath_basename(path,
10792 scratch_pool),
10793 walk_path ? walk_path : "",
10794 scratch_pool);
10795 path = svn_relpath_dirname(path, scratch_pool);
10796
10797 if (path[0] == '\0') /* No mergeinfo to inherit. */
10798 break;
10799 }
10800 }
10801
10802 if (mergeinfo_in_cat)
10803 {
10804 if (walk_path)
10805 SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(&mergeinfo_in_cat,
10806 mergeinfo_in_cat,
10807 walk_path,
10808 scratch_pool,
10809 scratch_pool));
10810 SVN_ERR(svn_mergeinfo_intersect2(&mergeinfo_in_cat,
10811 mergeinfo_in_cat, mergeinfo,
10812 TRUE,
10813 scratch_pool, scratch_pool));
10814 SVN_ERR(svn_mergeinfo__equals(in_catalog, mergeinfo_in_cat,
10815 mergeinfo, TRUE, scratch_pool));
10816 }
10817 }
10818
10819 return SVN_NO_ERROR;
10820 }
10821
10822 /* A svn_log_entry_receiver_t baton for log_find_operative_revs(). */
10823 typedef struct log_find_operative_baton_t
10824 {
10825 /* The catalog of explicit mergeinfo on a reintegrate source. */
10826 svn_mergeinfo_catalog_t merged_catalog;
10827
10828 /* The catalog of unmerged history from the reintegrate target to
10829 the source which we will create. Allocated in RESULT_POOL. */
10830 svn_mergeinfo_catalog_t unmerged_catalog;
10831
10832 /* The repository absolute path of the reintegrate target. */
10833 const char *target_fspath;
10834
10835 /* The path of the reintegrate source relative to the repository root. */
10836 const char *source_repos_rel_path;
10837
10838 apr_pool_t *result_pool;
10839 } log_find_operative_baton_t;
10840
10841 /* A svn_log_entry_receiver_t callback for find_unsynced_ranges(). */
10842 static svn_error_t *
log_find_operative_revs(void * baton,svn_log_entry_t * log_entry,apr_pool_t * pool)10843 log_find_operative_revs(void *baton,
10844 svn_log_entry_t *log_entry,
10845 apr_pool_t *pool)
10846 {
10847 log_find_operative_baton_t *log_baton = baton;
10848 apr_hash_index_t *hi;
10849 svn_revnum_t revision;
10850
10851 /* It's possible that authz restrictions on the merge source prevent us
10852 from knowing about any of the changes for LOG_ENTRY->REVISION. */
10853 if (!log_entry->changed_paths2)
10854 return SVN_NO_ERROR;
10855
10856 revision = log_entry->revision;
10857
10858 for (hi = apr_hash_first(pool, log_entry->changed_paths2);
10859 hi;
10860 hi = apr_hash_next(hi))
10861 {
10862 const char *subtree_missing_this_rev;
10863 const char *path = apr_hash_this_key(hi);
10864 const char *rel_path;
10865 const char *source_rel_path;
10866 svn_boolean_t in_catalog;
10867 svn_mergeinfo_t log_entry_as_mergeinfo;
10868
10869 rel_path = svn_fspath__skip_ancestor(log_baton->target_fspath, path);
10870 /* Easy out: The path is not within the tree of interest. */
10871 if (rel_path == NULL)
10872 continue;
10873
10874 source_rel_path = svn_relpath_join(log_baton->source_repos_rel_path,
10875 rel_path, pool);
10876
10877 SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo,
10878 apr_psprintf(pool, "%s:%ld",
10879 path, revision),
10880 pool));
10881
10882 SVN_ERR(mergeinfo_in_catalog(&in_catalog, &subtree_missing_this_rev,
10883 source_rel_path, log_entry_as_mergeinfo,
10884 log_baton->merged_catalog,
10885 pool, pool));
10886
10887 if (!in_catalog)
10888 {
10889 svn_mergeinfo_t unmerged_for_key;
10890 const char *suffix, *missing_path;
10891
10892 /* If there is no mergeinfo on the source tree we'll say
10893 the "subtree" missing this revision is the root of the
10894 source. */
10895 if (!subtree_missing_this_rev)
10896 subtree_missing_this_rev = log_baton->source_repos_rel_path;
10897
10898 suffix = svn_relpath_skip_ancestor(subtree_missing_this_rev,
10899 source_rel_path);
10900 if (suffix && suffix[0] != '\0')
10901 {
10902 missing_path = apr_pstrmemdup(pool, path,
10903 strlen(path) - strlen(suffix) - 1);
10904 }
10905 else
10906 {
10907 missing_path = path;
10908 }
10909
10910 SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo,
10911 apr_psprintf(pool, "%s:%ld",
10912 missing_path, revision),
10913 log_baton->result_pool));
10914 unmerged_for_key = svn_hash_gets(log_baton->unmerged_catalog,
10915 subtree_missing_this_rev);
10916
10917 if (unmerged_for_key)
10918 {
10919 SVN_ERR(svn_mergeinfo_merge2(unmerged_for_key,
10920 log_entry_as_mergeinfo,
10921 log_baton->result_pool,
10922 pool));
10923 }
10924 else
10925 {
10926 svn_hash_sets(log_baton->unmerged_catalog,
10927 apr_pstrdup(log_baton->result_pool,
10928 subtree_missing_this_rev),
10929 log_entry_as_mergeinfo);
10930 }
10931
10932 }
10933 }
10934 return SVN_NO_ERROR;
10935 }
10936
10937 /* Determine if the mergeinfo on a reintegrate source SOURCE_LOC,
10938 reflects that the source is fully synced with the reintegrate target
10939 TARGET_LOC, even if a naive interpretation of the source's
10940 mergeinfo says otherwise -- See issue #3577.
10941
10942 UNMERGED_CATALOG represents the history (as mergeinfo) from
10943 TARGET_LOC that is not represented in SOURCE_LOC's
10944 explicit/inherited mergeinfo as represented by MERGED_CATALOG.
10945 MERGED_CATALOG may be empty if the source has no explicit or inherited
10946 mergeinfo.
10947
10948 Check that all of the unmerged revisions in UNMERGED_CATALOG's
10949 mergeinfos are "phantoms", that is, one of the following conditions holds:
10950
10951 1) The revision affects no corresponding paths in SOURCE_LOC.
10952
10953 2) The revision affects corresponding paths in SOURCE_LOC,
10954 but based on the mergeinfo in MERGED_CATALOG, the change was
10955 previously merged.
10956
10957 Make a deep copy, allocated in RESULT_POOL, of any portions of
10958 UNMERGED_CATALOG that are not phantoms, to TRUE_UNMERGED_CATALOG.
10959
10960 Note: The keys in all mergeinfo catalogs used here are relative to the
10961 root of the repository.
10962
10963 RA_SESSION is an RA session open to the repository of TARGET_LOC; it may
10964 be temporarily reparented within this function.
10965
10966 Use SCRATCH_POOL for all temporary allocations. */
10967 static svn_error_t *
find_unsynced_ranges(const svn_client__pathrev_t * source_loc,const svn_client__pathrev_t * target_loc,svn_mergeinfo_catalog_t unmerged_catalog,svn_mergeinfo_catalog_t merged_catalog,svn_mergeinfo_catalog_t true_unmerged_catalog,svn_ra_session_t * ra_session,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10968 find_unsynced_ranges(const svn_client__pathrev_t *source_loc,
10969 const svn_client__pathrev_t *target_loc,
10970 svn_mergeinfo_catalog_t unmerged_catalog,
10971 svn_mergeinfo_catalog_t merged_catalog,
10972 svn_mergeinfo_catalog_t true_unmerged_catalog,
10973 svn_ra_session_t *ra_session,
10974 apr_pool_t *result_pool,
10975 apr_pool_t *scratch_pool)
10976 {
10977 svn_rangelist_t *potentially_unmerged_ranges = NULL;
10978
10979 /* Convert all the unmerged history to a rangelist. */
10980 if (apr_hash_count(unmerged_catalog))
10981 {
10982 apr_hash_index_t *hi_catalog;
10983
10984 potentially_unmerged_ranges =
10985 apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
10986
10987 for (hi_catalog = apr_hash_first(scratch_pool, unmerged_catalog);
10988 hi_catalog;
10989 hi_catalog = apr_hash_next(hi_catalog))
10990 {
10991 svn_mergeinfo_t mergeinfo = apr_hash_this_val(hi_catalog);
10992
10993 SVN_ERR(svn_rangelist__merge_many(potentially_unmerged_ranges,
10994 mergeinfo,
10995 scratch_pool, scratch_pool));
10996 }
10997 }
10998
10999 /* Find any unmerged revisions which both affect the source and
11000 are not yet merged to it. */
11001 if (potentially_unmerged_ranges)
11002 {
11003 svn_revnum_t oldest_rev =
11004 (APR_ARRAY_IDX(potentially_unmerged_ranges,
11005 0,
11006 svn_merge_range_t *))->start + 1;
11007 svn_revnum_t youngest_rev =
11008 (APR_ARRAY_IDX(potentially_unmerged_ranges,
11009 potentially_unmerged_ranges->nelts - 1,
11010 svn_merge_range_t *))->end;
11011 log_find_operative_baton_t log_baton;
11012 const char *old_session_url = NULL;
11013 svn_error_t *err;
11014
11015 log_baton.merged_catalog = merged_catalog;
11016 log_baton.unmerged_catalog = true_unmerged_catalog;
11017 log_baton.source_repos_rel_path
11018 = svn_client__pathrev_relpath(source_loc, scratch_pool);
11019 log_baton.target_fspath
11020 = svn_client__pathrev_fspath(target_loc, scratch_pool);
11021 log_baton.result_pool = result_pool;
11022
11023 /* Reparent the session to TARGET_LOC if this target location
11024 * exists within the unmerged revision range. */
11025 if (target_loc->rev <= youngest_rev && target_loc->rev >= oldest_rev)
11026 SVN_ERR(svn_client__ensure_ra_session_url(
11027 &old_session_url, ra_session, target_loc->url, scratch_pool));
11028
11029 err = get_log(ra_session, "", youngest_rev, oldest_rev,
11030 TRUE, /* discover_changed_paths */
11031 log_find_operative_revs, &log_baton,
11032 scratch_pool);
11033 if (old_session_url)
11034 err = svn_error_compose_create(err,
11035 svn_ra_reparent(ra_session,
11036 old_session_url,
11037 scratch_pool));
11038 SVN_ERR(err);
11039 }
11040
11041 return SVN_NO_ERROR;
11042 }
11043
11044
11045 /* Find the youngest revision that has been merged from target to source.
11046 *
11047 * If any location in TARGET_HISTORY_AS_MERGEINFO is mentioned in
11048 * SOURCE_MERGEINFO, then we know that at least one merge was done from the
11049 * target to the source. In that case, set *YOUNGEST_MERGED_REV to the
11050 * youngest revision of that intersection (unless *YOUNGEST_MERGED_REV is
11051 * already younger than that). Otherwise, leave *YOUNGEST_MERGED_REV alone.
11052 */
11053 static svn_error_t *
find_youngest_merged_rev(svn_revnum_t * youngest_merged_rev,svn_mergeinfo_t target_history_as_mergeinfo,svn_mergeinfo_t source_mergeinfo,apr_pool_t * scratch_pool)11054 find_youngest_merged_rev(svn_revnum_t *youngest_merged_rev,
11055 svn_mergeinfo_t target_history_as_mergeinfo,
11056 svn_mergeinfo_t source_mergeinfo,
11057 apr_pool_t *scratch_pool)
11058 {
11059 svn_mergeinfo_t explicit_source_target_history_intersection;
11060
11061 SVN_ERR(svn_mergeinfo_intersect2(
11062 &explicit_source_target_history_intersection,
11063 source_mergeinfo, target_history_as_mergeinfo, TRUE,
11064 scratch_pool, scratch_pool));
11065 if (apr_hash_count(explicit_source_target_history_intersection))
11066 {
11067 svn_revnum_t old_rev, young_rev;
11068
11069 /* Keep track of the youngest revision merged from target to source. */
11070 SVN_ERR(svn_mergeinfo__get_range_endpoints(
11071 &young_rev, &old_rev,
11072 explicit_source_target_history_intersection, scratch_pool));
11073 if (!SVN_IS_VALID_REVNUM(*youngest_merged_rev)
11074 || (young_rev > *youngest_merged_rev))
11075 *youngest_merged_rev = young_rev;
11076 }
11077
11078 return SVN_NO_ERROR;
11079 }
11080
11081 /* Set *FILTERED_MERGEINFO_P to the parts of TARGET_HISTORY_AS_MERGEINFO
11082 * that are not present in the source branch.
11083 *
11084 * SOURCE_MERGEINFO is the explicit or inherited mergeinfo of the source
11085 * branch SOURCE_PATHREV. Extend SOURCE_MERGEINFO, modifying it in
11086 * place, to include the natural history (implicit mergeinfo) of
11087 * SOURCE_PATHREV. ### But make these additions in SCRATCH_POOL.
11088 *
11089 * SOURCE_RA_SESSION is an RA session open to the repository containing
11090 * SOURCE_PATHREV; it may be temporarily reparented within this function.
11091 *
11092 * ### [JAF] This function is named '..._subroutine' simply because I
11093 * factored it out based on code similarity, without knowing what it's
11094 * purpose is. We should clarify its purpose and choose a better name.
11095 */
11096 static svn_error_t *
find_unmerged_mergeinfo_subroutine(svn_mergeinfo_t * filtered_mergeinfo_p,svn_mergeinfo_t target_history_as_mergeinfo,svn_mergeinfo_t source_mergeinfo,const svn_client__pathrev_t * source_pathrev,svn_ra_session_t * source_ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11097 find_unmerged_mergeinfo_subroutine(svn_mergeinfo_t *filtered_mergeinfo_p,
11098 svn_mergeinfo_t target_history_as_mergeinfo,
11099 svn_mergeinfo_t source_mergeinfo,
11100 const svn_client__pathrev_t *source_pathrev,
11101 svn_ra_session_t *source_ra_session,
11102 svn_client_ctx_t *ctx,
11103 apr_pool_t *result_pool,
11104 apr_pool_t *scratch_pool)
11105 {
11106 svn_mergeinfo_t source_history_as_mergeinfo;
11107
11108 /* Get the source path's natural history and merge it into source
11109 path's explicit or inherited mergeinfo. */
11110 SVN_ERR(svn_client__get_history_as_mergeinfo(
11111 &source_history_as_mergeinfo, NULL /* has_rev_zero_history */,
11112 source_pathrev, source_pathrev->rev, SVN_INVALID_REVNUM,
11113 source_ra_session, ctx, scratch_pool));
11114 SVN_ERR(svn_mergeinfo_merge2(source_mergeinfo,
11115 source_history_as_mergeinfo,
11116 scratch_pool, scratch_pool));
11117
11118 /* Now source_mergeinfo represents everything we know about
11119 source_path's history. Now we need to know what part, if any, of the
11120 corresponding target's history is *not* part of source_path's total
11121 history; because it is neither shared history nor was it ever merged
11122 from the target to the source. */
11123 SVN_ERR(svn_mergeinfo_remove2(filtered_mergeinfo_p,
11124 source_mergeinfo,
11125 target_history_as_mergeinfo, TRUE,
11126 result_pool, scratch_pool));
11127 return SVN_NO_ERROR;
11128 }
11129
11130 /* Helper for calculate_left_hand_side() which produces a mergeinfo catalog
11131 describing what parts of of the reintegrate target have not previously been
11132 merged to the reintegrate source.
11133
11134 SOURCE_CATALOG is the collection of explicit mergeinfo on SOURCE_LOC and
11135 all its children, i.e. the mergeinfo catalog for the reintegrate source.
11136
11137 TARGET_HISTORY_HASH is a hash of (const char *) paths mapped to
11138 svn_mergeinfo_t representing the location history. Each of these
11139 path keys represent a path in the reintegrate target, relative to the
11140 repository root, which has explicit mergeinfo and/or is the reintegrate
11141 target itself. The svn_mergeinfo_t's contain the natural history of each
11142 path@TARGET_REV. Effectively this is the mergeinfo catalog on the
11143 reintegrate target.
11144
11145 YC_ANCESTOR_REV is the revision of the youngest common ancestor of the
11146 reintegrate source and the reintegrate target.
11147
11148 SOURCE_LOC is the reintegrate source.
11149
11150 SOURCE_RA_SESSION is a session opened to the URL of SOURCE_LOC
11151 and TARGET_RA_SESSION is open to TARGET->loc.url.
11152
11153 For each entry in TARGET_HISTORY_HASH check that the history it
11154 represents is contained in either the explicit mergeinfo for the
11155 corresponding path in SOURCE_CATALOG, the corresponding path's inherited
11156 mergeinfo (if no explicit mergeinfo for the path is found in
11157 SOURCE_CATALOG), or the corresponding path's natural history. Populate
11158 *UNMERGED_TO_SOURCE_CATALOG with the corresponding source paths mapped to
11159 the mergeinfo from the target's natural history which is *not* found. Also
11160 include any mergeinfo from SOURCE_CATALOG which explicitly describes the
11161 target's history but for which *no* entry was found in
11162 TARGET_HISTORY_HASH.
11163
11164 If no part of TARGET_HISTORY_HASH is found in SOURCE_CATALOG set
11165 *YOUNGEST_MERGED_REV to SVN_INVALID_REVNUM; otherwise set it to the youngest
11166 revision previously merged from the target to the source, and filter
11167 *UNMERGED_TO_SOURCE_CATALOG so that it contains no ranges greater than
11168 *YOUNGEST_MERGED_REV.
11169
11170 *UNMERGED_TO_SOURCE_CATALOG is (deeply) allocated in RESULT_POOL.
11171 SCRATCH_POOL is used for all temporary allocations. */
11172 static svn_error_t *
find_unmerged_mergeinfo(svn_mergeinfo_catalog_t * unmerged_to_source_catalog,svn_revnum_t * youngest_merged_rev,svn_revnum_t yc_ancestor_rev,svn_mergeinfo_catalog_t source_catalog,apr_hash_t * target_history_hash,const svn_client__pathrev_t * source_loc,const merge_target_t * target,svn_ra_session_t * source_ra_session,svn_ra_session_t * target_ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11173 find_unmerged_mergeinfo(svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
11174 svn_revnum_t *youngest_merged_rev,
11175 svn_revnum_t yc_ancestor_rev,
11176 svn_mergeinfo_catalog_t source_catalog,
11177 apr_hash_t *target_history_hash,
11178 const svn_client__pathrev_t *source_loc,
11179 const merge_target_t *target,
11180 svn_ra_session_t *source_ra_session,
11181 svn_ra_session_t *target_ra_session,
11182 svn_client_ctx_t *ctx,
11183 apr_pool_t *result_pool,
11184 apr_pool_t *scratch_pool)
11185 {
11186 const char *source_repos_rel_path
11187 = svn_client__pathrev_relpath(source_loc, scratch_pool);
11188 const char *target_repos_rel_path
11189 = svn_client__pathrev_relpath(&target->loc, scratch_pool);
11190 apr_hash_index_t *hi;
11191 svn_mergeinfo_catalog_t new_catalog = apr_hash_make(result_pool);
11192 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
11193
11194 assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11195 assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11196
11197 *youngest_merged_rev = SVN_INVALID_REVNUM;
11198
11199 /* Examine the natural history of each path in the reintegrate target
11200 with explicit mergeinfo. */
11201 for (hi = apr_hash_first(scratch_pool, target_history_hash);
11202 hi;
11203 hi = apr_hash_next(hi))
11204 {
11205 const char *target_path = apr_hash_this_key(hi);
11206 svn_mergeinfo_t target_history_as_mergeinfo = apr_hash_this_val(hi);
11207 const char *path_rel_to_session
11208 = svn_relpath_skip_ancestor(target_repos_rel_path, target_path);
11209 const char *source_path;
11210 svn_client__pathrev_t *source_pathrev;
11211 svn_mergeinfo_t source_mergeinfo, filtered_mergeinfo;
11212
11213 svn_pool_clear(iterpool);
11214
11215 source_path = svn_relpath_join(source_repos_rel_path,
11216 path_rel_to_session, iterpool);
11217 source_pathrev = svn_client__pathrev_join_relpath(
11218 source_loc, path_rel_to_session, iterpool);
11219
11220 /* Remove any target history that is also part of the source's history,
11221 i.e. their common ancestry. By definition this has already been
11222 "merged" from the target to the source. If the source has explicit
11223 self referential mergeinfo it would intersect with the target's
11224 history below, making it appear that some merges had been done from
11225 the target to the source, when this might not actually be the case. */
11226 SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
11227 &target_history_as_mergeinfo, target_history_as_mergeinfo,
11228 source_loc->rev, yc_ancestor_rev, TRUE, iterpool, iterpool));
11229
11230 /* Look for any explicit mergeinfo on the source path corresponding to
11231 the target path. If we find any remove that from SOURCE_CATALOG.
11232 When this iteration over TARGET_HISTORY_HASH is complete all that
11233 should be left in SOURCE_CATALOG are subtrees that have explicit
11234 mergeinfo on the reintegrate source where there is no corresponding
11235 explicit mergeinfo on the reintegrate target. */
11236 source_mergeinfo = svn_hash_gets(source_catalog, source_path);
11237 if (source_mergeinfo)
11238 {
11239 svn_hash_sets(source_catalog, source_path, NULL);
11240
11241 SVN_ERR(find_youngest_merged_rev(youngest_merged_rev,
11242 target_history_as_mergeinfo,
11243 source_mergeinfo,
11244 iterpool));
11245 }
11246 else
11247 {
11248 /* There is no mergeinfo on source_path *or* source_path doesn't
11249 exist at all. If simply doesn't exist we can ignore it
11250 altogether. */
11251 svn_node_kind_t kind;
11252
11253 SVN_ERR(svn_ra_check_path(source_ra_session,
11254 path_rel_to_session,
11255 source_loc->rev, &kind, iterpool));
11256 if (kind == svn_node_none)
11257 continue;
11258 /* Else source_path does exist though it has no explicit mergeinfo.
11259 Find its inherited mergeinfo. If it doesn't have any then simply
11260 set source_mergeinfo to an empty hash. */
11261 SVN_ERR(svn_client__get_repos_mergeinfo(
11262 &source_mergeinfo, source_ra_session,
11263 source_pathrev->url, source_pathrev->rev,
11264 svn_mergeinfo_inherited, FALSE /*squelch_incapable*/,
11265 iterpool));
11266 if (!source_mergeinfo)
11267 source_mergeinfo = apr_hash_make(iterpool);
11268 }
11269
11270 /* Use scratch_pool rather than iterpool because filtered_mergeinfo
11271 is going into new_catalog below and needs to last to the end of
11272 this function. */
11273 SVN_ERR(find_unmerged_mergeinfo_subroutine(
11274 &filtered_mergeinfo, target_history_as_mergeinfo,
11275 source_mergeinfo, source_pathrev,
11276 source_ra_session, ctx, scratch_pool, iterpool));
11277 svn_hash_sets(new_catalog, apr_pstrdup(scratch_pool, source_path),
11278 filtered_mergeinfo);
11279 }
11280
11281 /* Are there any subtrees with explicit mergeinfo still left in the merge
11282 source where there was no explicit mergeinfo for the corresponding path
11283 in the merge target? If so, add the intersection of those path's
11284 mergeinfo and the corresponding target path's mergeinfo to
11285 new_catalog. */
11286 for (hi = apr_hash_first(scratch_pool, source_catalog);
11287 hi;
11288 hi = apr_hash_next(hi))
11289 {
11290 const char *source_path = apr_hash_this_key(hi);
11291 const char *path_rel_to_session =
11292 svn_relpath_skip_ancestor(source_repos_rel_path, source_path);
11293 const char *source_url;
11294 svn_mergeinfo_t source_mergeinfo = apr_hash_this_val(hi);
11295 svn_mergeinfo_t filtered_mergeinfo;
11296 svn_client__pathrev_t *target_pathrev;
11297 svn_mergeinfo_t target_history_as_mergeinfo;
11298 svn_error_t *err;
11299
11300 svn_pool_clear(iterpool);
11301
11302 source_url = svn_path_url_add_component2(source_loc->url,
11303 path_rel_to_session, iterpool);
11304 target_pathrev = svn_client__pathrev_join_relpath(
11305 &target->loc, path_rel_to_session, iterpool);
11306 err = svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo,
11307 NULL /* has_rev_zero_history */,
11308 target_pathrev,
11309 target->loc.rev,
11310 SVN_INVALID_REVNUM,
11311 target_ra_session,
11312 ctx, iterpool);
11313 if (err)
11314 {
11315 if (err->apr_err == SVN_ERR_FS_NOT_FOUND
11316 || err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
11317 {
11318 /* This path with explicit mergeinfo in the source doesn't
11319 exist on the target. */
11320 svn_error_clear(err);
11321 err = NULL;
11322 }
11323 else
11324 {
11325 return svn_error_trace(err);
11326 }
11327 }
11328 else
11329 {
11330 svn_client__pathrev_t *pathrev;
11331
11332 SVN_ERR(find_youngest_merged_rev(youngest_merged_rev,
11333 target_history_as_mergeinfo,
11334 source_mergeinfo,
11335 iterpool));
11336
11337 /* Use scratch_pool rather than iterpool because filtered_mergeinfo
11338 is going into new_catalog below and needs to last to the end of
11339 this function. */
11340 /* ### Why looking at SOURCE_url at TARGET_rev? */
11341 SVN_ERR(svn_client__pathrev_create_with_session(
11342 &pathrev, source_ra_session, target->loc.rev, source_url,
11343 iterpool));
11344 SVN_ERR(find_unmerged_mergeinfo_subroutine(
11345 &filtered_mergeinfo, target_history_as_mergeinfo,
11346 source_mergeinfo, pathrev,
11347 source_ra_session, ctx, scratch_pool, iterpool));
11348 if (apr_hash_count(filtered_mergeinfo))
11349 svn_hash_sets(new_catalog,
11350 apr_pstrdup(scratch_pool, source_path),
11351 filtered_mergeinfo);
11352 }
11353 }
11354
11355 /* Limit new_catalog to the youngest revisions previously merged from
11356 the target to the source. */
11357 if (SVN_IS_VALID_REVNUM(*youngest_merged_rev))
11358 SVN_ERR(svn_mergeinfo__filter_catalog_by_ranges(&new_catalog,
11359 new_catalog,
11360 *youngest_merged_rev,
11361 0, /* No oldest bound. */
11362 TRUE,
11363 scratch_pool,
11364 scratch_pool));
11365
11366 /* Make a shiny new copy before blowing away all the temporary pools. */
11367 *unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(new_catalog,
11368 result_pool);
11369 svn_pool_destroy(iterpool);
11370 return SVN_NO_ERROR;
11371 }
11372
11373 /* Helper for svn_client_merge_reintegrate() which calculates the
11374 'left hand side' of the underlying two-URL merge that a --reintegrate
11375 merge actually performs. If no merge should be performed, set
11376 *LEFT_P to NULL.
11377
11378 TARGET->abspath is the absolute working copy path of the reintegrate
11379 merge.
11380
11381 SOURCE_LOC is the reintegrate source.
11382
11383 SUBTREES_WITH_MERGEINFO is a hash of (const char *) absolute paths mapped
11384 to (svn_mergeinfo_t *) mergeinfo values for each working copy path with
11385 explicit mergeinfo in TARGET->abspath. Actually we only need to know the
11386 paths, not the mergeinfo.
11387
11388 TARGET->loc.rev is the working revision the entire WC tree rooted at
11389 TARGET is at.
11390
11391 Populate *UNMERGED_TO_SOURCE_CATALOG with the mergeinfo describing what
11392 parts of TARGET->loc have not been merged to SOURCE_LOC, up to the
11393 youngest revision ever merged from the TARGET->abspath to the source if
11394 such exists, see doc string for find_unmerged_mergeinfo().
11395
11396 SOURCE_RA_SESSION is a session opened to the SOURCE_LOC
11397 and TARGET_RA_SESSION is open to TARGET->loc.url.
11398
11399 *LEFT_P, *MERGED_TO_SOURCE_CATALOG , and *UNMERGED_TO_SOURCE_CATALOG are
11400 allocated in RESULT_POOL. SCRATCH_POOL is used for all temporary
11401 allocations. */
11402 static svn_error_t *
calculate_left_hand_side(svn_client__pathrev_t ** left_p,svn_mergeinfo_catalog_t * merged_to_source_catalog,svn_mergeinfo_catalog_t * unmerged_to_source_catalog,const merge_target_t * target,apr_hash_t * subtrees_with_mergeinfo,const svn_client__pathrev_t * source_loc,svn_ra_session_t * source_ra_session,svn_ra_session_t * target_ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11403 calculate_left_hand_side(svn_client__pathrev_t **left_p,
11404 svn_mergeinfo_catalog_t *merged_to_source_catalog,
11405 svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
11406 const merge_target_t *target,
11407 apr_hash_t *subtrees_with_mergeinfo,
11408 const svn_client__pathrev_t *source_loc,
11409 svn_ra_session_t *source_ra_session,
11410 svn_ra_session_t *target_ra_session,
11411 svn_client_ctx_t *ctx,
11412 apr_pool_t *result_pool,
11413 apr_pool_t *scratch_pool)
11414 {
11415 svn_mergeinfo_catalog_t mergeinfo_catalog, unmerged_catalog;
11416 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
11417 apr_hash_index_t *hi;
11418 /* hash of paths mapped to arrays of svn_mergeinfo_t. */
11419 apr_hash_t *target_history_hash = apr_hash_make(scratch_pool);
11420 svn_revnum_t youngest_merged_rev;
11421 svn_client__pathrev_t *yc_ancestor;
11422
11423 assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11424 assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11425
11426 /* Initialize our return variables. */
11427 *left_p = NULL;
11428
11429 /* TARGET->abspath may not have explicit mergeinfo and thus may not be
11430 contained within SUBTREES_WITH_MERGEINFO. If this is the case then
11431 add a dummy item for TARGET->abspath so we get its history (i.e. implicit
11432 mergeinfo) below. */
11433 if (!svn_hash_gets(subtrees_with_mergeinfo, target->abspath))
11434 svn_hash_sets(subtrees_with_mergeinfo, target->abspath,
11435 apr_hash_make(result_pool));
11436
11437 /* Get the history segments (as mergeinfo) for TARGET->abspath and any of
11438 its subtrees with explicit mergeinfo. */
11439 for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
11440 hi;
11441 hi = apr_hash_next(hi))
11442 {
11443 const char *local_abspath = apr_hash_this_key(hi);
11444 svn_client__pathrev_t *target_child;
11445 const char *repos_relpath;
11446 svn_mergeinfo_t target_history_as_mergeinfo;
11447
11448 svn_pool_clear(iterpool);
11449
11450 /* Convert the absolute path with mergeinfo on it to a path relative
11451 to the session root. */
11452 SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, NULL, NULL,
11453 ctx->wc_ctx, local_abspath,
11454 scratch_pool, iterpool));
11455 target_child = svn_client__pathrev_create_with_relpath(
11456 target->loc.repos_root_url, target->loc.repos_uuid,
11457 target->loc.rev, repos_relpath, iterpool);
11458 SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo,
11459 NULL /* has_rev_zero_hist */,
11460 target_child,
11461 target->loc.rev,
11462 SVN_INVALID_REVNUM,
11463 target_ra_session,
11464 ctx, scratch_pool));
11465
11466 svn_hash_sets(target_history_hash, repos_relpath,
11467 target_history_as_mergeinfo);
11468 }
11469
11470 /* Check that SOURCE_LOC and TARGET->loc are
11471 actually related, we can't reintegrate if they are not. Also
11472 get an initial value for the YCA revision number. */
11473 SVN_ERR(svn_client__get_youngest_common_ancestor(
11474 &yc_ancestor, source_loc, &target->loc, target_ra_session, ctx,
11475 iterpool, iterpool));
11476 if (! yc_ancestor)
11477 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11478 _("'%s@%ld' must be ancestrally related to "
11479 "'%s@%ld'"), source_loc->url, source_loc->rev,
11480 target->loc.url, target->loc.rev);
11481
11482 /* If the source revision is the same as the youngest common
11483 revision, then there can't possibly be any unmerged revisions
11484 that we need to apply to target. */
11485 if (source_loc->rev == yc_ancestor->rev)
11486 {
11487 svn_pool_destroy(iterpool);
11488 return SVN_NO_ERROR;
11489 }
11490
11491 /* Get the mergeinfo from the source, including its descendants
11492 with differing explicit mergeinfo. */
11493 SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
11494 &mergeinfo_catalog, source_ra_session,
11495 source_loc->url, source_loc->rev,
11496 svn_mergeinfo_inherited, FALSE /* squelch_incapable */,
11497 TRUE /* include_descendants */, iterpool, iterpool));
11498
11499 if (!mergeinfo_catalog)
11500 mergeinfo_catalog = apr_hash_make(iterpool);
11501
11502 *merged_to_source_catalog = svn_mergeinfo_catalog_dup(mergeinfo_catalog,
11503 result_pool);
11504
11505 /* Filter the source's mergeinfo catalog so that we are left with
11506 mergeinfo that describes what has *not* previously been merged from
11507 TARGET->loc to SOURCE_LOC. */
11508 SVN_ERR(find_unmerged_mergeinfo(&unmerged_catalog,
11509 &youngest_merged_rev,
11510 yc_ancestor->rev,
11511 mergeinfo_catalog,
11512 target_history_hash,
11513 source_loc,
11514 target,
11515 source_ra_session,
11516 target_ra_session,
11517 ctx,
11518 iterpool, iterpool));
11519
11520 /* Simplify unmerged_catalog through elision then make a copy in POOL. */
11521 SVN_ERR(svn_client__elide_mergeinfo_catalog(unmerged_catalog,
11522 iterpool));
11523 *unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(unmerged_catalog,
11524 result_pool);
11525
11526 if (youngest_merged_rev == SVN_INVALID_REVNUM)
11527 {
11528 /* We never merged to the source. Just return the branch point. */
11529 *left_p = svn_client__pathrev_dup(yc_ancestor, result_pool);
11530 }
11531 else
11532 {
11533 /* We've previously merged some or all of the target, up to
11534 youngest_merged_rev, to the source. Set
11535 *LEFT_P to cover the youngest part of this range. */
11536 SVN_ERR(svn_client__repos_location(left_p, target_ra_session,
11537 &target->loc, youngest_merged_rev,
11538 ctx, result_pool, iterpool));
11539 }
11540
11541 svn_pool_destroy(iterpool);
11542 return SVN_NO_ERROR;
11543 }
11544
11545 /* Determine the URLs and revisions needed to perform a reintegrate merge
11546 * from SOURCE_LOC into the working copy at TARGET.
11547 *
11548 * SOURCE_RA_SESSION and TARGET_RA_SESSION are RA sessions opened to the
11549 * URLs of SOURCE_LOC and TARGET->loc respectively.
11550 *
11551 * Set *SOURCE_P to
11552 * the source-left and source-right locations of the required merge. Set
11553 * *YC_ANCESTOR_P to the location of the youngest ancestor.
11554 * Any of these output pointers may be NULL if not wanted.
11555 *
11556 * See svn_client_find_reintegrate_merge() for other details.
11557 */
11558 static svn_error_t *
find_reintegrate_merge(merge_source_t ** source_p,svn_client__pathrev_t ** yc_ancestor_p,svn_ra_session_t * source_ra_session,const svn_client__pathrev_t * source_loc,svn_ra_session_t * target_ra_session,const merge_target_t * target,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11559 find_reintegrate_merge(merge_source_t **source_p,
11560 svn_client__pathrev_t **yc_ancestor_p,
11561 svn_ra_session_t *source_ra_session,
11562 const svn_client__pathrev_t *source_loc,
11563 svn_ra_session_t *target_ra_session,
11564 const merge_target_t *target,
11565 svn_client_ctx_t *ctx,
11566 apr_pool_t *result_pool,
11567 apr_pool_t *scratch_pool)
11568 {
11569 svn_client__pathrev_t *yc_ancestor;
11570 svn_client__pathrev_t *loc1;
11571 merge_source_t source;
11572 svn_mergeinfo_catalog_t unmerged_to_source_mergeinfo_catalog;
11573 svn_mergeinfo_catalog_t merged_to_source_mergeinfo_catalog;
11574 svn_error_t *err;
11575 apr_hash_t *subtrees_with_mergeinfo;
11576
11577 assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11578 assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11579
11580 /* As the WC tree is "pure", use its last-updated-to revision as
11581 the default revision for the left side of our merge, since that's
11582 what the repository sub-tree is required to be up to date with
11583 (with regard to the WC). */
11584 /* ### Bogus/obsolete comment? */
11585
11586 /* Can't reintegrate to or from the root of the repository. */
11587 if (strcmp(source_loc->url, source_loc->repos_root_url) == 0
11588 || strcmp(target->loc.url, target->loc.repos_root_url) == 0)
11589 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11590 _("Neither the reintegrate source nor target "
11591 "can be the root of the repository"));
11592
11593 /* Find all the subtrees in TARGET_WCPATH that have explicit mergeinfo. */
11594 err = get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
11595 target->abspath, svn_depth_infinity,
11596 ctx, scratch_pool, scratch_pool);
11597 /* Issue #3896: If invalid mergeinfo in the reintegrate target
11598 prevents us from proceeding, then raise the best error possible. */
11599 if (err && err->apr_err == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
11600 err = svn_error_quick_wrap(err, _("Reintegrate merge not possible"));
11601 SVN_ERR(err);
11602
11603 SVN_ERR(calculate_left_hand_side(&loc1,
11604 &merged_to_source_mergeinfo_catalog,
11605 &unmerged_to_source_mergeinfo_catalog,
11606 target,
11607 subtrees_with_mergeinfo,
11608 source_loc,
11609 source_ra_session,
11610 target_ra_session,
11611 ctx,
11612 scratch_pool, scratch_pool));
11613
11614 /* Did calculate_left_hand_side() decide that there was no merge to
11615 be performed here? */
11616 if (! loc1)
11617 {
11618 if (source_p)
11619 *source_p = NULL;
11620 if (yc_ancestor_p)
11621 *yc_ancestor_p = NULL;
11622 return SVN_NO_ERROR;
11623 }
11624
11625 source.loc1 = loc1;
11626 source.loc2 = source_loc;
11627
11628 /* If the target was moved after the source was branched from it,
11629 it is possible that the left URL differs from the target's current
11630 URL. If so, then adjust TARGET_RA_SESSION to point to the old URL. */
11631 if (strcmp(source.loc1->url, target->loc.url))
11632 SVN_ERR(svn_ra_reparent(target_ra_session, source.loc1->url, scratch_pool));
11633
11634 SVN_ERR(svn_client__get_youngest_common_ancestor(
11635 &yc_ancestor, source.loc2, source.loc1, target_ra_session,
11636 ctx, scratch_pool, scratch_pool));
11637
11638 if (! yc_ancestor)
11639 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11640 _("'%s@%ld' must be ancestrally related to "
11641 "'%s@%ld'"),
11642 source.loc1->url, source.loc1->rev,
11643 source.loc2->url, source.loc2->rev);
11644
11645 /* The source side of a reintegrate merge is not 'ancestral', except in
11646 * the degenerate case where source == YCA. */
11647 source.ancestral = (loc1->rev == yc_ancestor->rev);
11648
11649 if (source.loc1->rev > yc_ancestor->rev)
11650 {
11651 /* Have we actually merged anything to the source from the
11652 target? If so, make sure we've merged a contiguous
11653 prefix. */
11654 svn_mergeinfo_catalog_t final_unmerged_catalog = apr_hash_make(scratch_pool);
11655
11656 SVN_ERR(find_unsynced_ranges(source_loc, &target->loc,
11657 unmerged_to_source_mergeinfo_catalog,
11658 merged_to_source_mergeinfo_catalog,
11659 final_unmerged_catalog,
11660 target_ra_session, scratch_pool,
11661 scratch_pool));
11662
11663 if (apr_hash_count(final_unmerged_catalog))
11664 {
11665 svn_string_t *source_mergeinfo_cat_string;
11666
11667 SVN_ERR(svn_mergeinfo__catalog_to_formatted_string(
11668 &source_mergeinfo_cat_string,
11669 final_unmerged_catalog,
11670 " ", _(" Missing ranges: "), scratch_pool));
11671 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
11672 NULL,
11673 _("Reintegrate can only be used if "
11674 "revisions %ld through %ld were "
11675 "previously merged from %s to the "
11676 "reintegrate source, but this is "
11677 "not the case:\n%s"),
11678 yc_ancestor->rev + 1, source.loc2->rev,
11679 target->loc.url,
11680 source_mergeinfo_cat_string->data);
11681 }
11682 }
11683
11684 /* Left side: trunk@youngest-trunk-rev-merged-to-branch-at-specified-peg-rev
11685 * Right side: branch@specified-peg-revision */
11686 if (source_p)
11687 *source_p = merge_source_dup(&source, result_pool);
11688
11689 if (yc_ancestor_p)
11690 *yc_ancestor_p = svn_client__pathrev_dup(yc_ancestor, result_pool);
11691 return SVN_NO_ERROR;
11692 }
11693
11694 /* Resolve the source and target locations and open RA sessions to them, and
11695 * perform some checks appropriate for a reintegrate merge.
11696 *
11697 * Set *SOURCE_RA_SESSION_P and *SOURCE_LOC_P to a new session and the
11698 * repository location of SOURCE_PATH_OR_URL at SOURCE_PEG_REVISION. Set
11699 * *TARGET_RA_SESSION_P and *TARGET_P to a new session and the repository
11700 * location of the WC at TARGET_ABSPATH.
11701 *
11702 * Throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES error if the target WC node is
11703 * a locally added node or if the source and target are not in the same
11704 * repository. Throw a SVN_ERR_CLIENT_NOT_READY_TO_MERGE error if the
11705 * target WC is not at a single revision without switched subtrees and
11706 * without local mods.
11707 *
11708 * Allocate all the outputs in RESULT_POOL.
11709 */
11710 static svn_error_t *
open_reintegrate_source_and_target(svn_ra_session_t ** source_ra_session_p,svn_client__pathrev_t ** source_loc_p,svn_ra_session_t ** target_ra_session_p,merge_target_t ** target_p,const char * source_path_or_url,const svn_opt_revision_t * source_peg_revision,const char * target_abspath,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11711 open_reintegrate_source_and_target(svn_ra_session_t **source_ra_session_p,
11712 svn_client__pathrev_t **source_loc_p,
11713 svn_ra_session_t **target_ra_session_p,
11714 merge_target_t **target_p,
11715 const char *source_path_or_url,
11716 const svn_opt_revision_t *source_peg_revision,
11717 const char *target_abspath,
11718 svn_client_ctx_t *ctx,
11719 apr_pool_t *result_pool,
11720 apr_pool_t *scratch_pool)
11721 {
11722 svn_client__pathrev_t *source_loc;
11723 merge_target_t *target;
11724
11725 /* Open the target WC. A reintegrate merge requires the merge target to
11726 * reflect a subtree of the repository as found at a single revision. */
11727 SVN_ERR(open_target_wc(&target, target_abspath,
11728 FALSE, FALSE, FALSE,
11729 ctx, scratch_pool, scratch_pool));
11730 if (! target->loc.url)
11731 return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
11732 _("Can't reintegrate into '%s' because it is "
11733 "locally added and therefore not related to "
11734 "the merge source"),
11735 svn_dirent_local_style(target->abspath,
11736 scratch_pool));
11737
11738 SVN_ERR(svn_client_open_ra_session2(target_ra_session_p,
11739 target->loc.url, target->abspath,
11740 ctx, result_pool, scratch_pool));
11741
11742 SVN_ERR(svn_client__ra_session_from_path2(
11743 source_ra_session_p, &source_loc,
11744 source_path_or_url, NULL, source_peg_revision, source_peg_revision,
11745 ctx, result_pool));
11746
11747 /* source_loc and target->loc are required to be in the same repository,
11748 as mergeinfo doesn't come into play for cross-repository merging. */
11749 SVN_ERR(check_same_repos(source_loc,
11750 svn_dirent_local_style(source_path_or_url,
11751 scratch_pool),
11752 &target->loc,
11753 svn_dirent_local_style(target->abspath,
11754 scratch_pool),
11755 TRUE /* strict_urls */, scratch_pool));
11756
11757 *source_loc_p = source_loc;
11758 *target_p = target;
11759 return SVN_NO_ERROR;
11760 }
11761
11762 /* The body of svn_client_merge_reintegrate(), which see for details. */
11763 static svn_error_t *
merge_reintegrate_locked(svn_client__conflict_report_t ** conflict_report,const char * source_path_or_url,const svn_opt_revision_t * source_peg_revision,const char * target_abspath,svn_boolean_t diff_ignore_ancestry,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)11764 merge_reintegrate_locked(svn_client__conflict_report_t **conflict_report,
11765 const char *source_path_or_url,
11766 const svn_opt_revision_t *source_peg_revision,
11767 const char *target_abspath,
11768 svn_boolean_t diff_ignore_ancestry,
11769 svn_boolean_t dry_run,
11770 const apr_array_header_t *merge_options,
11771 svn_client_ctx_t *ctx,
11772 apr_pool_t *result_pool,
11773 apr_pool_t *scratch_pool)
11774 {
11775 svn_ra_session_t *target_ra_session, *source_ra_session;
11776 merge_target_t *target;
11777 svn_client__pathrev_t *source_loc;
11778 merge_source_t *source;
11779 svn_client__pathrev_t *yc_ancestor;
11780 svn_boolean_t use_sleep = FALSE;
11781 svn_error_t *err;
11782
11783 SVN_ERR(open_reintegrate_source_and_target(
11784 &source_ra_session, &source_loc, &target_ra_session, &target,
11785 source_path_or_url, source_peg_revision, target_abspath,
11786 ctx, scratch_pool, scratch_pool));
11787
11788 SVN_ERR(find_reintegrate_merge(&source, &yc_ancestor,
11789 source_ra_session, source_loc,
11790 target_ra_session, target,
11791 ctx, scratch_pool, scratch_pool));
11792
11793 if (! source)
11794 {
11795 *conflict_report = NULL;
11796 return SVN_NO_ERROR;
11797 }
11798
11799 /* Do the real merge! */
11800 /* ### TODO(reint): Make sure that one isn't the same line ancestor
11801 ### of the other (what's erroneously referred to as "ancestrally
11802 ### related" in this source file). For now, we just say the source
11803 ### isn't "ancestral" even if it is (in the degenerate case where
11804 ### source-left equals YCA). */
11805 source->ancestral = FALSE;
11806 err = merge_cousins_and_supplement_mergeinfo(conflict_report,
11807 &use_sleep,
11808 target,
11809 target_ra_session,
11810 source_ra_session,
11811 source, yc_ancestor,
11812 TRUE /* same_repos */,
11813 svn_depth_infinity,
11814 diff_ignore_ancestry,
11815 FALSE /* force_delete */,
11816 FALSE /* record_only */,
11817 dry_run,
11818 merge_options,
11819 ctx,
11820 result_pool, scratch_pool);
11821
11822 if (use_sleep)
11823 svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
11824
11825 SVN_ERR(err);
11826 return SVN_NO_ERROR;
11827 }
11828
11829 svn_error_t *
svn_client_merge_reintegrate(const char * source_path_or_url,const svn_opt_revision_t * source_peg_revision,const char * target_wcpath,svn_boolean_t dry_run,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * pool)11830 svn_client_merge_reintegrate(const char *source_path_or_url,
11831 const svn_opt_revision_t *source_peg_revision,
11832 const char *target_wcpath,
11833 svn_boolean_t dry_run,
11834 const apr_array_header_t *merge_options,
11835 svn_client_ctx_t *ctx,
11836 apr_pool_t *pool)
11837 {
11838 const char *target_abspath, *lock_abspath;
11839 svn_client__conflict_report_t *conflict_report;
11840
11841 SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
11842 target_wcpath, ctx, pool));
11843
11844 if (!dry_run)
11845 SVN_WC__CALL_WITH_WRITE_LOCK(
11846 merge_reintegrate_locked(&conflict_report,
11847 source_path_or_url, source_peg_revision,
11848 target_abspath,
11849 FALSE /*diff_ignore_ancestry*/,
11850 dry_run, merge_options, ctx, pool, pool),
11851 ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
11852 else
11853 SVN_ERR(merge_reintegrate_locked(&conflict_report,
11854 source_path_or_url, source_peg_revision,
11855 target_abspath,
11856 FALSE /*diff_ignore_ancestry*/,
11857 dry_run, merge_options, ctx, pool, pool));
11858
11859 SVN_ERR(svn_client__make_merge_conflict_error(conflict_report, pool));
11860 return SVN_NO_ERROR;
11861 }
11862
11863
11864 /* The body of svn_client_merge_peg5(), which see for details.
11865 *
11866 * IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge().
11867 */
11868 static svn_error_t *
merge_peg_locked(svn_client__conflict_report_t ** conflict_report,const char * source_path_or_url,const svn_opt_revision_t * source_peg_revision,const svn_rangelist_t * ranges_to_merge,const char * target_abspath,svn_depth_t depth,svn_boolean_t ignore_mergeinfo,svn_boolean_t diff_ignore_ancestry,svn_boolean_t force_delete,svn_boolean_t record_only,svn_boolean_t dry_run,svn_boolean_t allow_mixed_rev,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11869 merge_peg_locked(svn_client__conflict_report_t **conflict_report,
11870 const char *source_path_or_url,
11871 const svn_opt_revision_t *source_peg_revision,
11872 const svn_rangelist_t *ranges_to_merge,
11873 const char *target_abspath,
11874 svn_depth_t depth,
11875 svn_boolean_t ignore_mergeinfo,
11876 svn_boolean_t diff_ignore_ancestry,
11877 svn_boolean_t force_delete,
11878 svn_boolean_t record_only,
11879 svn_boolean_t dry_run,
11880 svn_boolean_t allow_mixed_rev,
11881 const apr_array_header_t *merge_options,
11882 svn_client_ctx_t *ctx,
11883 apr_pool_t *result_pool,
11884 apr_pool_t *scratch_pool)
11885 {
11886 merge_target_t *target;
11887 svn_client__pathrev_t *source_loc;
11888 apr_array_header_t *merge_sources;
11889 svn_ra_session_t *ra_session;
11890 apr_pool_t *sesspool;
11891 svn_boolean_t use_sleep = FALSE;
11892 svn_error_t *err;
11893 svn_boolean_t same_repos;
11894
11895 SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
11896
11897 SVN_ERR(open_target_wc(&target, target_abspath,
11898 allow_mixed_rev, TRUE, TRUE,
11899 ctx, scratch_pool, scratch_pool));
11900
11901 /* Create a short lived session pool */
11902 sesspool = svn_pool_create(scratch_pool);
11903
11904 /* Open an RA session to our source URL, and determine its root URL. */
11905 SVN_ERR(svn_client__ra_session_from_path2(
11906 &ra_session, &source_loc,
11907 source_path_or_url, NULL, source_peg_revision, source_peg_revision,
11908 ctx, sesspool));
11909
11910 /* Normalize our merge sources. */
11911 SVN_ERR(normalize_merge_sources(&merge_sources, source_path_or_url,
11912 source_loc,
11913 ranges_to_merge, ra_session, ctx,
11914 scratch_pool, scratch_pool));
11915
11916 /* Check for same_repos. */
11917 same_repos = is_same_repos(&target->loc, source_loc, TRUE /* strict_urls */);
11918
11919 /* Do the real merge! (We say with confidence that our merge
11920 sources are both ancestral and related.) */
11921 if (getenv("SVN_ELEMENT_MERGE")
11922 && same_repos
11923 && (depth == svn_depth_infinity || depth == svn_depth_unknown)
11924 && ignore_mergeinfo
11925 && !record_only)
11926 {
11927 err = svn_client__merge_elements(&use_sleep,
11928 merge_sources, target, ra_session,
11929 diff_ignore_ancestry, force_delete,
11930 dry_run, merge_options,
11931 ctx, result_pool, scratch_pool);
11932 /* ### Currently this merge just errors out on any conflicts */
11933 *conflict_report = NULL;
11934 }
11935 else
11936 err = do_merge(NULL, NULL, conflict_report, &use_sleep,
11937 merge_sources, target, ra_session,
11938 TRUE /*sources_related*/, same_repos, ignore_mergeinfo,
11939 diff_ignore_ancestry, force_delete, dry_run,
11940 record_only, NULL, FALSE, FALSE, depth, merge_options,
11941 ctx, result_pool, scratch_pool);
11942
11943 /* We're done with our RA session. */
11944 svn_pool_destroy(sesspool);
11945
11946 if (use_sleep)
11947 svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
11948
11949 SVN_ERR(err);
11950 return SVN_NO_ERROR;
11951 }
11952
11953 /* Details of an automatic merge. */
11954 typedef struct automatic_merge_t
11955 {
11956 svn_client__pathrev_t *yca, *base, *right, *target;
11957 svn_boolean_t is_reintegrate_like;
11958 svn_boolean_t allow_mixed_rev, allow_local_mods, allow_switched_subtrees;
11959 } automatic_merge_t;
11960
11961 static svn_error_t *
11962 client_find_automatic_merge(automatic_merge_t **merge_p,
11963 const char *source_path_or_url,
11964 const svn_opt_revision_t *source_revision,
11965 const char *target_abspath,
11966 svn_boolean_t allow_mixed_rev,
11967 svn_boolean_t allow_local_mods,
11968 svn_boolean_t allow_switched_subtrees,
11969 svn_client_ctx_t *ctx,
11970 apr_pool_t *result_pool,
11971 apr_pool_t *scratch_pool);
11972
11973 static svn_error_t *
11974 do_automatic_merge_locked(svn_client__conflict_report_t **conflict_report,
11975 const automatic_merge_t *merge,
11976 const char *target_abspath,
11977 svn_depth_t depth,
11978 svn_boolean_t diff_ignore_ancestry,
11979 svn_boolean_t force_delete,
11980 svn_boolean_t record_only,
11981 svn_boolean_t dry_run,
11982 const apr_array_header_t *merge_options,
11983 svn_client_ctx_t *ctx,
11984 apr_pool_t *result_pool,
11985 apr_pool_t *scratch_pool);
11986
11987 svn_error_t *
svn_client_merge_peg5(const char * source_path_or_url,const apr_array_header_t * ranges_to_merge,const svn_opt_revision_t * source_peg_revision,const char * target_wcpath,svn_depth_t depth,svn_boolean_t ignore_mergeinfo,svn_boolean_t diff_ignore_ancestry,svn_boolean_t force_delete,svn_boolean_t record_only,svn_boolean_t dry_run,svn_boolean_t allow_mixed_rev,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * pool)11988 svn_client_merge_peg5(const char *source_path_or_url,
11989 const apr_array_header_t *ranges_to_merge,
11990 const svn_opt_revision_t *source_peg_revision,
11991 const char *target_wcpath,
11992 svn_depth_t depth,
11993 svn_boolean_t ignore_mergeinfo,
11994 svn_boolean_t diff_ignore_ancestry,
11995 svn_boolean_t force_delete,
11996 svn_boolean_t record_only,
11997 svn_boolean_t dry_run,
11998 svn_boolean_t allow_mixed_rev,
11999 const apr_array_header_t *merge_options,
12000 svn_client_ctx_t *ctx,
12001 apr_pool_t *pool)
12002 {
12003 const char *target_abspath, *lock_abspath;
12004 svn_client__conflict_report_t *conflict_report;
12005
12006 /* No ranges to merge? No problem. */
12007 if (ranges_to_merge != NULL && ranges_to_merge->nelts == 0)
12008 return SVN_NO_ERROR;
12009
12010 SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
12011 target_wcpath, ctx, pool));
12012
12013 /* Do an automatic merge if no revision ranges are specified. */
12014 if (ranges_to_merge == NULL)
12015 {
12016 automatic_merge_t *merge;
12017
12018 if (ignore_mergeinfo)
12019 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12020 _("Cannot merge automatically while "
12021 "ignoring mergeinfo"));
12022
12023 /* Find the details of the merge needed. */
12024 SVN_ERR(client_find_automatic_merge(
12025 &merge,
12026 source_path_or_url, source_peg_revision,
12027 target_abspath,
12028 allow_mixed_rev,
12029 TRUE /*allow_local_mods*/,
12030 TRUE /*allow_switched_subtrees*/,
12031 ctx, pool, pool));
12032
12033 if (!dry_run)
12034 SVN_WC__CALL_WITH_WRITE_LOCK(
12035 do_automatic_merge_locked(&conflict_report,
12036 merge,
12037 target_abspath, depth,
12038 diff_ignore_ancestry,
12039 force_delete, record_only, dry_run,
12040 merge_options, ctx, pool, pool),
12041 ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
12042 else
12043 SVN_ERR(do_automatic_merge_locked(&conflict_report,
12044 merge,
12045 target_abspath, depth,
12046 diff_ignore_ancestry,
12047 force_delete, record_only, dry_run,
12048 merge_options, ctx, pool, pool));
12049 }
12050 else if (!dry_run)
12051 SVN_WC__CALL_WITH_WRITE_LOCK(
12052 merge_peg_locked(&conflict_report,
12053 source_path_or_url, source_peg_revision,
12054 ranges_to_merge,
12055 target_abspath, depth, ignore_mergeinfo,
12056 diff_ignore_ancestry,
12057 force_delete, record_only, dry_run,
12058 allow_mixed_rev, merge_options, ctx, pool, pool),
12059 ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
12060 else
12061 SVN_ERR(merge_peg_locked(&conflict_report,
12062 source_path_or_url, source_peg_revision,
12063 ranges_to_merge,
12064 target_abspath, depth, ignore_mergeinfo,
12065 diff_ignore_ancestry,
12066 force_delete, record_only, dry_run,
12067 allow_mixed_rev, merge_options, ctx, pool, pool));
12068
12069 SVN_ERR(svn_client__make_merge_conflict_error(conflict_report, pool));
12070 return SVN_NO_ERROR;
12071 }
12072
12073
12074 /* The location-history of a branch.
12075 *
12076 * This structure holds the set of path-revisions occupied by a branch,
12077 * from an externally chosen 'tip' location back to its origin. The
12078 * 'tip' location is the youngest location that we are considering on
12079 * the branch. */
12080 typedef struct branch_history_t
12081 {
12082 /* The tip location of the branch. That is, the youngest location that's
12083 * in the repository and that we're considering. If we're considering a
12084 * target branch right up to an uncommitted WC, then this is the WC base
12085 * (pristine) location. */
12086 svn_client__pathrev_t *tip;
12087 /* The location-segment history, as mergeinfo. */
12088 svn_mergeinfo_t history;
12089 /* Whether the location-segment history reached as far as (necessarily
12090 the root path in) revision 0 -- a fact that can't be represented as
12091 mergeinfo. */
12092 svn_boolean_t has_r0_history;
12093 } branch_history_t;
12094
12095 /* Return the location on BRANCH_HISTORY at revision REV, or NULL if none. */
12096 static svn_client__pathrev_t *
location_on_branch_at_rev(const branch_history_t * branch_history,svn_revnum_t rev,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12097 location_on_branch_at_rev(const branch_history_t *branch_history,
12098 svn_revnum_t rev,
12099 apr_pool_t *result_pool,
12100 apr_pool_t *scratch_pool)
12101 {
12102 apr_hash_index_t *hi;
12103
12104 for (hi = apr_hash_first(scratch_pool, branch_history->history); hi;
12105 hi = apr_hash_next(hi))
12106 {
12107 const char *fspath = apr_hash_this_key(hi);
12108 svn_rangelist_t *rangelist = apr_hash_this_val(hi);
12109 int i;
12110
12111 for (i = 0; i < rangelist->nelts; i++)
12112 {
12113 svn_merge_range_t *r = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
12114 if (r->start < rev && rev <= r->end)
12115 {
12116 return svn_client__pathrev_create_with_relpath(
12117 branch_history->tip->repos_root_url,
12118 branch_history->tip->repos_uuid,
12119 rev, fspath + 1, result_pool);
12120 }
12121 }
12122 }
12123 return NULL;
12124 }
12125
12126 /* */
12127 typedef struct source_and_target_t
12128 {
12129 svn_client__pathrev_t *source;
12130 svn_ra_session_t *source_ra_session;
12131 branch_history_t source_branch;
12132
12133 merge_target_t *target;
12134 svn_ra_session_t *target_ra_session;
12135 branch_history_t target_branch;
12136
12137 /* Repos location of the youngest common ancestor of SOURCE and TARGET. */
12138 svn_client__pathrev_t *yca;
12139 } source_and_target_t;
12140
12141 /* Set *INTERSECTION_P to the intersection of BRANCH_HISTORY with the
12142 * revision range OLDEST_REV to YOUNGEST_REV (inclusive).
12143 *
12144 * If the intersection is empty, the result will be a branch history object
12145 * containing an empty (not null) history.
12146 *
12147 * ### The 'tip' of the result is currently unchanged.
12148 */
12149 static svn_error_t *
branch_history_intersect_range(branch_history_t ** intersection_p,const branch_history_t * branch_history,svn_revnum_t oldest_rev,svn_revnum_t youngest_rev,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12150 branch_history_intersect_range(branch_history_t **intersection_p,
12151 const branch_history_t *branch_history,
12152 svn_revnum_t oldest_rev,
12153 svn_revnum_t youngest_rev,
12154 apr_pool_t *result_pool,
12155 apr_pool_t *scratch_pool)
12156 {
12157 branch_history_t *result = apr_palloc(result_pool, sizeof(*result));
12158
12159 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
12160 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
12161 SVN_ERR_ASSERT(oldest_rev >= 1);
12162 /* Allow a just-empty range (oldest = youngest + 1) but not an
12163 * arbitrary reverse range (such as oldest = youngest + 2). */
12164 SVN_ERR_ASSERT(oldest_rev <= youngest_rev + 1);
12165
12166 if (oldest_rev <= youngest_rev)
12167 {
12168 SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
12169 &result->history, branch_history->history,
12170 youngest_rev, oldest_rev - 1, TRUE /* include_range */,
12171 result_pool, scratch_pool));
12172 result->history = svn_mergeinfo_dup(result->history, result_pool);
12173 }
12174 else
12175 {
12176 result->history = apr_hash_make(result_pool);
12177 }
12178 result->has_r0_history = FALSE;
12179
12180 /* ### TODO: Set RESULT->tip to the tip of the intersection. */
12181 result->tip = svn_client__pathrev_dup(branch_history->tip, result_pool);
12182
12183 *intersection_p = result;
12184 return SVN_NO_ERROR;
12185 }
12186
12187 /* Set *OLDEST_P and *YOUNGEST_P to the oldest and youngest locations
12188 * (inclusive) along BRANCH. OLDEST_P and/or YOUNGEST_P may be NULL if not
12189 * wanted.
12190 */
12191 static svn_error_t *
branch_history_get_endpoints(svn_client__pathrev_t ** oldest_p,svn_client__pathrev_t ** youngest_p,const branch_history_t * branch,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12192 branch_history_get_endpoints(svn_client__pathrev_t **oldest_p,
12193 svn_client__pathrev_t **youngest_p,
12194 const branch_history_t *branch,
12195 apr_pool_t *result_pool,
12196 apr_pool_t *scratch_pool)
12197 {
12198 svn_revnum_t youngest_rev, oldest_rev;
12199
12200 SVN_ERR(svn_mergeinfo__get_range_endpoints(
12201 &youngest_rev, &oldest_rev,
12202 branch->history, scratch_pool));
12203 if (oldest_p)
12204 *oldest_p = location_on_branch_at_rev(
12205 branch, oldest_rev + 1, result_pool, scratch_pool);
12206 if (youngest_p)
12207 *youngest_p = location_on_branch_at_rev(
12208 branch, youngest_rev, result_pool, scratch_pool);
12209 return SVN_NO_ERROR;
12210 }
12211
12212 /* Implements the svn_log_entry_receiver_t interface.
12213
12214 Set *BATON to LOG_ENTRY->revision and return SVN_ERR_CEASE_INVOCATION. */
12215 static svn_error_t *
operative_rev_receiver(void * baton,svn_log_entry_t * log_entry,apr_pool_t * pool)12216 operative_rev_receiver(void *baton,
12217 svn_log_entry_t *log_entry,
12218 apr_pool_t *pool)
12219 {
12220 svn_revnum_t *operative_rev = baton;
12221
12222 *operative_rev = log_entry->revision;
12223
12224 /* We've found the youngest merged or oldest eligible revision, so
12225 we're done...
12226
12227 ...but wait, shouldn't we care if LOG_ENTRY->NON_INHERITABLE is
12228 true? Because if it is, then LOG_ENTRY->REVISION is only
12229 partially merged/elgibile! And our only caller,
12230 find_last_merged_location (via short_circuit_mergeinfo_log) is
12231 interested in *fully* merged revisions. That's all true, but if
12232 find_last_merged_location() finds the youngest merged revision it
12233 will also check for the oldest eligible revision. So in the case
12234 the youngest merged rev is non-inheritable, the *same* non-inheritable
12235 rev will be found as the oldest eligible rev -- and
12236 find_last_merged_location() handles that situation. */
12237 return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
12238 }
12239
12240 /* Wrapper around svn_client__mergeinfo_log. All arguments are as per
12241 that private API. The discover_changed_paths, depth, and revprops args to
12242 svn_client__mergeinfo_log are always TRUE, svn_depth_infinity_t,
12243 and empty array respectively.
12244
12245 If RECEIVER raises a SVN_ERR_CEASE_INVOCATION error, but still sets
12246 *REVISION to a valid revnum, then clear the error. Otherwise return
12247 any error. */
12248 static svn_error_t*
short_circuit_mergeinfo_log(svn_mergeinfo_catalog_t * target_mergeinfo_cat,svn_boolean_t finding_merged,const char * target_path_or_url,const svn_opt_revision_t * target_peg_revision,const char * source_path_or_url,const svn_opt_revision_t * source_peg_revision,const svn_opt_revision_t * source_start_revision,const svn_opt_revision_t * source_end_revision,svn_log_entry_receiver_t receiver,svn_revnum_t * revision,svn_client_ctx_t * ctx,svn_ra_session_t * ra_session,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12249 short_circuit_mergeinfo_log(svn_mergeinfo_catalog_t *target_mergeinfo_cat,
12250 svn_boolean_t finding_merged,
12251 const char *target_path_or_url,
12252 const svn_opt_revision_t *target_peg_revision,
12253 const char *source_path_or_url,
12254 const svn_opt_revision_t *source_peg_revision,
12255 const svn_opt_revision_t *source_start_revision,
12256 const svn_opt_revision_t *source_end_revision,
12257 svn_log_entry_receiver_t receiver,
12258 svn_revnum_t *revision,
12259 svn_client_ctx_t *ctx,
12260 svn_ra_session_t *ra_session,
12261 apr_pool_t *result_pool,
12262 apr_pool_t *scratch_pool)
12263 {
12264 apr_array_header_t *revprops;
12265 svn_error_t *err;
12266 const char *session_url;
12267
12268 SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
12269
12270 revprops = apr_array_make(scratch_pool, 0, sizeof(const char *));
12271 err = svn_client__mergeinfo_log(finding_merged,
12272 target_path_or_url,
12273 target_peg_revision,
12274 target_mergeinfo_cat,
12275 source_path_or_url,
12276 source_peg_revision,
12277 source_start_revision,
12278 source_end_revision,
12279 receiver, revision,
12280 TRUE, svn_depth_infinity,
12281 revprops, ctx, ra_session,
12282 result_pool, scratch_pool);
12283
12284 err = svn_error_compose_create(
12285 err,
12286 svn_ra_reparent(ra_session, session_url, scratch_pool));
12287
12288 if (err)
12289 {
12290 /* We expect RECEIVER to short-circuit the (potentially expensive) log
12291 by raising an SVN_ERR_CEASE_INVOCATION -- see operative_rev_receiver.
12292 So we can ignore that error, but only as long as we actually found a
12293 valid revision. */
12294 if (SVN_IS_VALID_REVNUM(*revision)
12295 && err->apr_err == SVN_ERR_CEASE_INVOCATION)
12296 {
12297 svn_error_clear(err);
12298 err = NULL;
12299 }
12300 else
12301 {
12302 return svn_error_trace(err);
12303 }
12304 }
12305 return SVN_NO_ERROR;
12306 }
12307
12308 /* Set *BASE_P to the last location on SOURCE_BRANCH such that all changes
12309 * on SOURCE_BRANCH after YCA up to and including *BASE_P have already
12310 * been fully merged into TARGET.
12311 *
12312 * *BASE_P TIP
12313 * o-------o-----------o--- SOURCE_BRANCH
12314 * / \
12315 * -----o prev. \
12316 * YCA \ merges \
12317 * o-----------o----------- TARGET branch
12318 *
12319 * In terms of mergeinfo:
12320 *
12321 * Source a--... o=change, -=no-op revision
12322 * branch / \
12323 * YCA --> o a---o---o---o---o--- d=delete, a=add-as-a-copy
12324 *
12325 * Eligible -.eee.eeeeeeeeeeeeeeeeeeee .=not a source branch location
12326 *
12327 * Tgt-mi -.mmm.mm-mm-------m------- m=merged to root of TARGET or
12328 * subtree of TARGET with no
12329 * operative changes outside of that
12330 * subtree, -=not merged
12331 *
12332 * Eligible -.---.--e--eeeeeee-eeeeeee
12333 *
12334 * Next --------^----------------- BASE is just before here.
12335 *
12336 * / \
12337 * -----o prev. \
12338 * YCA \ merges \
12339 * o-----------o-------------
12340 *
12341 * If no revisions from SOURCE_BRANCH have been completely merged to TARGET,
12342 * then set *BASE_P to the YCA.
12343 */
12344 static svn_error_t *
find_last_merged_location(svn_client__pathrev_t ** base_p,svn_client__pathrev_t * yca,const branch_history_t * source_branch,svn_client__pathrev_t * target,svn_client_ctx_t * ctx,svn_ra_session_t * ra_session,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12345 find_last_merged_location(svn_client__pathrev_t **base_p,
12346 svn_client__pathrev_t *yca,
12347 const branch_history_t *source_branch,
12348 svn_client__pathrev_t *target,
12349 svn_client_ctx_t *ctx,
12350 svn_ra_session_t *ra_session,
12351 apr_pool_t *result_pool,
12352 apr_pool_t *scratch_pool)
12353 {
12354 svn_opt_revision_t source_peg_rev, source_start_rev, source_end_rev,
12355 target_opt_rev;
12356 svn_revnum_t youngest_merged_rev = SVN_INVALID_REVNUM;
12357 svn_mergeinfo_catalog_t target_mergeinfo_cat = NULL;
12358
12359 /* Using a local subpool for 'target_mergeinfo_cat' can make a big
12360 reduction in overall memory usage. */
12361 apr_pool_t *tmic_pool = svn_pool_create(scratch_pool);
12362
12363 source_peg_rev.kind = svn_opt_revision_number;
12364 source_peg_rev.value.number = source_branch->tip->rev;
12365 source_start_rev.kind = svn_opt_revision_number;
12366 source_start_rev.value.number = yca->rev;
12367 source_end_rev.kind = svn_opt_revision_number;
12368 source_end_rev.value.number = source_branch->tip->rev;
12369 target_opt_rev.kind = svn_opt_revision_number;
12370 target_opt_rev.value.number = target->rev;
12371
12372 /* Find the youngest revision fully merged from SOURCE_BRANCH to TARGET,
12373 if such a revision exists. */
12374 SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
12375 TRUE, /* Find merged */
12376 target->url, &target_opt_rev,
12377 source_branch->tip->url,
12378 &source_peg_rev,
12379 &source_end_rev, &source_start_rev,
12380 operative_rev_receiver,
12381 &youngest_merged_rev,
12382 ctx, ra_session,
12383 tmic_pool, tmic_pool));
12384
12385 if (!SVN_IS_VALID_REVNUM(youngest_merged_rev))
12386 {
12387 /* No revisions have been completely merged from SOURCE_BRANCH to
12388 TARGET so the base for the next merge is the YCA. */
12389 *base_p = yca;
12390 }
12391 else
12392 {
12393 /* One or more revisions have already been completely merged from
12394 SOURCE_BRANCH to TARGET, now find the oldest revision, older
12395 than the youngest merged revision, which is still eligible to
12396 be merged, if such exists. */
12397 branch_history_t *contiguous_source;
12398 svn_revnum_t base_rev;
12399 svn_revnum_t oldest_eligible_rev = SVN_INVALID_REVNUM;
12400
12401 /* If the only revisions eligible are younger than the youngest merged
12402 revision we can simply assume that the youngest eligible revision
12403 is the youngest merged revision. Obviously this may not be true!
12404 The revisions between the youngest merged revision and the tip of
12405 the branch may have several inoperative revisions -- they may *all*
12406 be inoperative revisions! But for the purpose of this function
12407 (i.e. finding the youngest revision after the YCA where all revs have
12408 been merged) that doesn't matter. */
12409 source_end_rev.value.number = youngest_merged_rev;
12410 SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
12411 FALSE, /* Find eligible */
12412 target->url, &target_opt_rev,
12413 source_branch->tip->url,
12414 &source_peg_rev,
12415 &source_start_rev, &source_end_rev,
12416 operative_rev_receiver,
12417 &oldest_eligible_rev,
12418 ctx, ra_session,
12419 tmic_pool, tmic_pool));
12420
12421 /* If there are revisions eligible for merging, use the oldest one
12422 to calculate the base. Otherwise there are no operative revisions
12423 to merge and we can simple set the base to the youngest revision
12424 already merged. */
12425 if (SVN_IS_VALID_REVNUM(oldest_eligible_rev))
12426 base_rev = oldest_eligible_rev - 1;
12427 else
12428 base_rev = youngest_merged_rev;
12429
12430 /* Find the branch location just before the oldest eligible rev.
12431 (We can't just use the base revs calculated above because the branch
12432 might have a gap there.) */
12433 SVN_ERR(branch_history_intersect_range(&contiguous_source,
12434 source_branch, yca->rev,
12435 base_rev,
12436 scratch_pool, scratch_pool));
12437 SVN_ERR(branch_history_get_endpoints(NULL, base_p, contiguous_source,
12438 result_pool, scratch_pool));
12439 }
12440
12441 svn_pool_destroy(tmic_pool);
12442 return SVN_NO_ERROR;
12443 }
12444
12445 /* Find a merge base location on the target branch, like in a sync
12446 * merge.
12447 *
12448 * BASE S_T->source
12449 * o-------o-----------o---
12450 * / \ \
12451 * -----o prev. \ \ this
12452 * YCA \ merge \ \ merge
12453 * o-----------o-----------o
12454 * S_T->target
12455 *
12456 * Set *BASE_P to BASE, the youngest location in the history of S_T->source
12457 * (at or after the YCA) at which all revisions up to BASE are effectively
12458 * merged into S_T->target.
12459 *
12460 * If no locations on the history of S_T->source are effectively merged to
12461 * S_T->target, set *BASE_P to the YCA.
12462 */
12463 static svn_error_t *
find_base_on_source(svn_client__pathrev_t ** base_p,source_and_target_t * s_t,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12464 find_base_on_source(svn_client__pathrev_t **base_p,
12465 source_and_target_t *s_t,
12466 svn_client_ctx_t *ctx,
12467 apr_pool_t *result_pool,
12468 apr_pool_t *scratch_pool)
12469 {
12470 SVN_ERR(find_last_merged_location(base_p,
12471 s_t->yca,
12472 &s_t->source_branch,
12473 s_t->target_branch.tip,
12474 ctx,
12475 s_t->source_ra_session,
12476 result_pool, scratch_pool));
12477 return SVN_NO_ERROR;
12478 }
12479
12480 /* Find a merge base location on the target branch, like in a reintegrate
12481 * merge.
12482 *
12483 * S_T->source
12484 * o-----------o-------o---
12485 * / prev. / \
12486 * -----o merge / \ this
12487 * YCA \ / \ merge
12488 * o-------o---------------o
12489 * BASE S_T->target
12490 *
12491 * Set *BASE_P to BASE, the youngest location in the history of S_T->target
12492 * (at or after the YCA) at which all revisions up to BASE are effectively
12493 * merged into S_T->source.
12494 *
12495 * If no locations on the history of S_T->target are effectively merged to
12496 * S_T->source, set *BASE_P to the YCA.
12497 */
12498 static svn_error_t *
find_base_on_target(svn_client__pathrev_t ** base_p,source_and_target_t * s_t,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12499 find_base_on_target(svn_client__pathrev_t **base_p,
12500 source_and_target_t *s_t,
12501 svn_client_ctx_t *ctx,
12502 apr_pool_t *result_pool,
12503 apr_pool_t *scratch_pool)
12504 {
12505 SVN_ERR(find_last_merged_location(base_p,
12506 s_t->yca,
12507 &s_t->target_branch,
12508 s_t->source,
12509 ctx,
12510 s_t->target_ra_session,
12511 result_pool, scratch_pool));
12512
12513 return SVN_NO_ERROR;
12514 }
12515
12516 /* Find the last point at which the branch at S_T->source was completely
12517 * merged to the branch at S_T->target or vice-versa.
12518 *
12519 * Fill in S_T->source_branch and S_T->target_branch and S_T->yca.
12520 * Set *BASE_P to the merge base. Set *IS_REINTEGRATE_LIKE to true if
12521 * an automatic merge from source to target would be a reintegration
12522 * merge: that is, if the last automatic merge was in the opposite
12523 * direction; or to false otherwise.
12524 *
12525 * If there is no youngest common ancestor, throw an error.
12526 */
12527 static svn_error_t *
find_automatic_merge(svn_client__pathrev_t ** base_p,svn_boolean_t * is_reintegrate_like,source_and_target_t * s_t,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12528 find_automatic_merge(svn_client__pathrev_t **base_p,
12529 svn_boolean_t *is_reintegrate_like,
12530 source_and_target_t *s_t,
12531 svn_client_ctx_t *ctx,
12532 apr_pool_t *result_pool,
12533 apr_pool_t *scratch_pool)
12534 {
12535 svn_client__pathrev_t *base_on_source, *base_on_target;
12536
12537 /* Get the location-history of each branch. */
12538 s_t->source_branch.tip = s_t->source;
12539 SVN_ERR(svn_client__get_history_as_mergeinfo(
12540 &s_t->source_branch.history, &s_t->source_branch.has_r0_history,
12541 s_t->source, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
12542 s_t->source_ra_session, ctx, scratch_pool));
12543 s_t->target_branch.tip = &s_t->target->loc;
12544 SVN_ERR(svn_client__get_history_as_mergeinfo(
12545 &s_t->target_branch.history, &s_t->target_branch.has_r0_history,
12546 &s_t->target->loc, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
12547 s_t->target_ra_session, ctx, scratch_pool));
12548
12549 SVN_ERR(svn_client__calc_youngest_common_ancestor(
12550 &s_t->yca, s_t->source, s_t->source_branch.history,
12551 s_t->source_branch.has_r0_history,
12552 &s_t->target->loc, s_t->target_branch.history,
12553 s_t->target_branch.has_r0_history,
12554 result_pool, scratch_pool));
12555
12556 if (! s_t->yca)
12557 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
12558 _("'%s@%ld' must be ancestrally related to "
12559 "'%s@%ld'"),
12560 s_t->source->url, s_t->source->rev,
12561 s_t->target->loc.url, s_t->target->loc.rev);
12562
12563 /* Find the latest revision of A synced to B and the latest
12564 * revision of B synced to A.
12565 *
12566 * base_on_source = youngest_complete_synced_point(source, target)
12567 * base_on_target = youngest_complete_synced_point(target, source)
12568 */
12569 SVN_ERR(find_base_on_source(&base_on_source, s_t,
12570 ctx, scratch_pool, scratch_pool));
12571 SVN_ERR(find_base_on_target(&base_on_target, s_t,
12572 ctx, scratch_pool, scratch_pool));
12573
12574 /* Choose a base. */
12575 if (base_on_source->rev >= base_on_target->rev)
12576 {
12577 *base_p = base_on_source;
12578 *is_reintegrate_like = FALSE;
12579 }
12580 else
12581 {
12582 *base_p = base_on_target;
12583 *is_reintegrate_like = TRUE;
12584 }
12585
12586 return SVN_NO_ERROR;
12587 }
12588
12589 /** Find out what kind of automatic merge would be needed, when the target
12590 * is only known as a repository location rather than a WC.
12591 *
12592 * Like find_automatic_merge() except that the target is
12593 * specified by @a target_path_or_url at @a target_revision, which must
12594 * refer to a repository location, instead of by a WC path argument.
12595 *
12596 * Set *MERGE_P to a new structure with all fields filled in except the
12597 * 'allow_*' flags.
12598 */
12599 static svn_error_t *
find_automatic_merge_no_wc(automatic_merge_t ** merge_p,const char * source_path_or_url,const svn_opt_revision_t * source_revision,const char * target_path_or_url,const svn_opt_revision_t * target_revision,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12600 find_automatic_merge_no_wc(automatic_merge_t **merge_p,
12601 const char *source_path_or_url,
12602 const svn_opt_revision_t *source_revision,
12603 const char *target_path_or_url,
12604 const svn_opt_revision_t *target_revision,
12605 svn_client_ctx_t *ctx,
12606 apr_pool_t *result_pool,
12607 apr_pool_t *scratch_pool)
12608 {
12609 source_and_target_t *s_t = apr_palloc(scratch_pool, sizeof(*s_t));
12610 svn_client__pathrev_t *target_loc;
12611 automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
12612
12613 /* Source */
12614 SVN_ERR(svn_client__ra_session_from_path2(
12615 &s_t->source_ra_session, &s_t->source,
12616 source_path_or_url, NULL, source_revision, source_revision,
12617 ctx, result_pool));
12618
12619 /* Target */
12620 SVN_ERR(svn_client__ra_session_from_path2(
12621 &s_t->target_ra_session, &target_loc,
12622 target_path_or_url, NULL, target_revision, target_revision,
12623 ctx, result_pool));
12624 s_t->target = apr_palloc(scratch_pool, sizeof(*s_t->target));
12625 s_t->target->abspath = NULL; /* indicate the target is not a WC */
12626 s_t->target->loc = *target_loc;
12627
12628 SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
12629 ctx, result_pool, scratch_pool));
12630
12631 merge->right = s_t->source;
12632 merge->target = &s_t->target->loc;
12633 merge->yca = s_t->yca;
12634 *merge_p = merge;
12635
12636 return SVN_NO_ERROR;
12637 }
12638
12639 /* Find the information needed to merge all unmerged changes from a source
12640 * branch into a target branch.
12641 *
12642 * Set @a *merge_p to the information needed to merge all unmerged changes
12643 * (up to @a source_revision) from the source branch @a source_path_or_url
12644 * at @a source_revision into the target WC at @a target_abspath.
12645 *
12646 * The flags @a allow_mixed_rev, @a allow_local_mods and
12647 * @a allow_switched_subtrees enable merging into a WC that is in any or all
12648 * of the states described by their names, but only if this function decides
12649 * that the merge will be in the same direction as the last automatic merge.
12650 * If, on the other hand, the last automatic merge was in the opposite
12651 * direction, then such states of the WC are not allowed regardless
12652 * of these flags. This function merely records these flags in the
12653 * @a *merge_p structure; do_automatic_merge_locked() checks the WC
12654 * state for compliance.
12655 *
12656 * Allocate the @a *merge_p structure in @a result_pool.
12657 */
12658 static svn_error_t *
client_find_automatic_merge(automatic_merge_t ** merge_p,const char * source_path_or_url,const svn_opt_revision_t * source_revision,const char * target_abspath,svn_boolean_t allow_mixed_rev,svn_boolean_t allow_local_mods,svn_boolean_t allow_switched_subtrees,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12659 client_find_automatic_merge(automatic_merge_t **merge_p,
12660 const char *source_path_or_url,
12661 const svn_opt_revision_t *source_revision,
12662 const char *target_abspath,
12663 svn_boolean_t allow_mixed_rev,
12664 svn_boolean_t allow_local_mods,
12665 svn_boolean_t allow_switched_subtrees,
12666 svn_client_ctx_t *ctx,
12667 apr_pool_t *result_pool,
12668 apr_pool_t *scratch_pool)
12669 {
12670 source_and_target_t *s_t = apr_palloc(result_pool, sizeof(*s_t));
12671 automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
12672
12673 SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
12674
12675 /* "Open" the target WC. Check the target WC for mixed-rev, local mods and
12676 * switched subtrees yet to faster exit and notify user before contacting
12677 * with server. After we find out what kind of merge is required, then if a
12678 * reintegrate-like merge is required we'll do the stricter checks, in
12679 * do_automatic_merge_locked(). */
12680 SVN_ERR(open_target_wc(&s_t->target, target_abspath,
12681 allow_mixed_rev,
12682 allow_local_mods,
12683 allow_switched_subtrees,
12684 ctx, result_pool, scratch_pool));
12685
12686 if (!s_t->target->loc.url)
12687 return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
12688 _("Can't perform automatic merge into '%s' "
12689 "because it is locally added and therefore "
12690 "not related to the merge source"),
12691 svn_dirent_local_style(target_abspath,
12692 scratch_pool));
12693
12694 /* Open RA sessions to the source and target trees. */
12695 SVN_ERR(svn_client_open_ra_session2(&s_t->target_ra_session,
12696 s_t->target->loc.url,
12697 s_t->target->abspath,
12698 ctx, result_pool, scratch_pool));
12699 SVN_ERR(svn_client__ra_session_from_path2(
12700 &s_t->source_ra_session, &s_t->source,
12701 source_path_or_url, NULL, source_revision, source_revision,
12702 ctx, result_pool));
12703
12704 /* Check source is in same repos as target. */
12705 SVN_ERR(check_same_repos(s_t->source, source_path_or_url,
12706 &s_t->target->loc, target_abspath,
12707 TRUE /* strict_urls */, scratch_pool));
12708
12709 SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
12710 ctx, result_pool, scratch_pool));
12711 merge->yca = s_t->yca;
12712 merge->right = s_t->source;
12713 merge->target = &s_t->target->loc;
12714 merge->allow_mixed_rev = allow_mixed_rev;
12715 merge->allow_local_mods = allow_local_mods;
12716 merge->allow_switched_subtrees = allow_switched_subtrees;
12717
12718 *merge_p = merge;
12719
12720 /* TODO: Close the source and target sessions here? */
12721
12722 return SVN_NO_ERROR;
12723 }
12724
12725 /* Perform an automatic merge, given the information in MERGE which
12726 * must have come from calling client_find_automatic_merge().
12727 *
12728 * Four locations are inputs: YCA, BASE, RIGHT, TARGET, as shown
12729 * depending on whether the base is on the source branch or the target
12730 * branch of this merge.
12731 *
12732 * RIGHT (is_reintegrate_like)
12733 * o-----------o-------o---
12734 * / prev. / \
12735 * -----o merge / \ this
12736 * YCA \ / \ merge
12737 * o-------o---------------o
12738 * BASE TARGET
12739 *
12740 * or
12741 *
12742 * BASE RIGHT (! is_reintegrate_like)
12743 * o-------o-----------o---
12744 * / \ \
12745 * -----o prev. \ \ this
12746 * YCA \ merge \ \ merge
12747 * o-----------o-----------o
12748 * TARGET
12749 *
12750 * ### TODO: The reintegrate-like code path does not yet
12751 * eliminate already-cherry-picked revisions from the source.
12752 */
12753 static svn_error_t *
do_automatic_merge_locked(svn_client__conflict_report_t ** conflict_report,const automatic_merge_t * merge,const char * target_abspath,svn_depth_t depth,svn_boolean_t diff_ignore_ancestry,svn_boolean_t force_delete,svn_boolean_t record_only,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)12754 do_automatic_merge_locked(svn_client__conflict_report_t **conflict_report,
12755 const automatic_merge_t *merge,
12756 const char *target_abspath,
12757 svn_depth_t depth,
12758 svn_boolean_t diff_ignore_ancestry,
12759 svn_boolean_t force_delete,
12760 svn_boolean_t record_only,
12761 svn_boolean_t dry_run,
12762 const apr_array_header_t *merge_options,
12763 svn_client_ctx_t *ctx,
12764 apr_pool_t *result_pool,
12765 apr_pool_t *scratch_pool)
12766 {
12767 merge_target_t *target;
12768 svn_boolean_t reintegrate_like = merge->is_reintegrate_like;
12769 svn_boolean_t use_sleep = FALSE;
12770 svn_error_t *err;
12771
12772 SVN_ERR(open_target_wc(&target, target_abspath,
12773 merge->allow_mixed_rev && ! reintegrate_like,
12774 merge->allow_local_mods && ! reintegrate_like,
12775 merge->allow_switched_subtrees && ! reintegrate_like,
12776 ctx, scratch_pool, scratch_pool));
12777
12778 if (reintegrate_like)
12779 {
12780 merge_source_t source;
12781 svn_ra_session_t *base_ra_session = NULL;
12782 svn_ra_session_t *right_ra_session = NULL;
12783 svn_ra_session_t *target_ra_session = NULL;
12784
12785 if (record_only)
12786 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12787 _("The required merge is reintegrate-like, "
12788 "and the record-only option "
12789 "cannot be used with this kind of merge"));
12790
12791 if (depth != svn_depth_unknown)
12792 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12793 _("The required merge is reintegrate-like, "
12794 "and the depth option "
12795 "cannot be used with this kind of merge"));
12796
12797 if (force_delete)
12798 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12799 _("The required merge is reintegrate-like, "
12800 "and the force_delete option "
12801 "cannot be used with this kind of merge"));
12802
12803 SVN_ERR(ensure_ra_session_url(&base_ra_session, merge->base->url,
12804 target->abspath, ctx, scratch_pool));
12805 SVN_ERR(ensure_ra_session_url(&right_ra_session, merge->right->url,
12806 target->abspath, ctx, scratch_pool));
12807 SVN_ERR(ensure_ra_session_url(&target_ra_session, target->loc.url,
12808 target->abspath, ctx, scratch_pool));
12809
12810 /* Check for and reject any abnormalities -- such as revisions that
12811 * have not yet been merged in the opposite direction -- that a
12812 * 'reintegrate' merge would have rejected. */
12813 {
12814 merge_source_t *source2;
12815
12816 SVN_ERR(find_reintegrate_merge(&source2, NULL,
12817 right_ra_session, merge->right,
12818 target_ra_session, target,
12819 ctx, scratch_pool, scratch_pool));
12820 }
12821
12822 source.loc1 = merge->base;
12823 source.loc2 = merge->right;
12824 source.ancestral = ! merge->is_reintegrate_like;
12825
12826 err = merge_cousins_and_supplement_mergeinfo(conflict_report,
12827 &use_sleep,
12828 target,
12829 base_ra_session,
12830 right_ra_session,
12831 &source, merge->yca,
12832 TRUE /* same_repos */,
12833 depth,
12834 FALSE /*diff_ignore_ancestry*/,
12835 force_delete, record_only,
12836 dry_run,
12837 merge_options,
12838 ctx,
12839 result_pool, scratch_pool);
12840 }
12841 else /* ! merge->is_reintegrate_like */
12842 {
12843 /* Ignoring the base that we found, we pass the YCA instead and let
12844 do_merge() work out which subtrees need which revision ranges to
12845 be merged. This enables do_merge() to fill in revision-range
12846 gaps that are older than the base that we calculated (which is
12847 for the root path of the merge).
12848
12849 An improvement would be to change find_automatic_merge() to
12850 find the base for each sutree, and then here use the oldest base
12851 among all subtrees. */
12852 apr_array_header_t *merge_sources;
12853 svn_ra_session_t *ra_session = NULL;
12854
12855 /* Normalize our merge sources, do_merge() requires this. See the
12856 'MERGEINFO MERGE SOURCE NORMALIZATION' global comment. */
12857 SVN_ERR(ensure_ra_session_url(&ra_session, merge->right->url,
12858 target->abspath, ctx, scratch_pool));
12859 SVN_ERR(normalize_merge_sources_internal(
12860 &merge_sources, merge->right,
12861 svn_rangelist__initialize(merge->yca->rev, merge->right->rev, TRUE,
12862 scratch_pool),
12863 ra_session, ctx, scratch_pool, scratch_pool));
12864
12865 err = do_merge(NULL, NULL, conflict_report, &use_sleep,
12866 merge_sources, target, ra_session,
12867 TRUE /*related*/, TRUE /*same_repos*/,
12868 FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
12869 force_delete, dry_run,
12870 record_only, NULL, FALSE, FALSE, depth, merge_options,
12871 ctx, result_pool, scratch_pool);
12872 }
12873
12874 if (use_sleep)
12875 svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
12876
12877 SVN_ERR(err);
12878
12879 return SVN_NO_ERROR;
12880 }
12881
12882 svn_error_t *
svn_client_get_merging_summary(svn_boolean_t * needs_reintegration,const char ** yca_url,svn_revnum_t * yca_rev,const char ** base_url,svn_revnum_t * base_rev,const char ** right_url,svn_revnum_t * right_rev,const char ** target_url,svn_revnum_t * target_rev,const char ** repos_root_url,const char * source_path_or_url,const svn_opt_revision_t * source_revision,const char * target_path_or_url,const svn_opt_revision_t * target_revision,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12883 svn_client_get_merging_summary(svn_boolean_t *needs_reintegration,
12884 const char **yca_url, svn_revnum_t *yca_rev,
12885 const char **base_url, svn_revnum_t *base_rev,
12886 const char **right_url, svn_revnum_t *right_rev,
12887 const char **target_url, svn_revnum_t *target_rev,
12888 const char **repos_root_url,
12889 const char *source_path_or_url,
12890 const svn_opt_revision_t *source_revision,
12891 const char *target_path_or_url,
12892 const svn_opt_revision_t *target_revision,
12893 svn_client_ctx_t *ctx,
12894 apr_pool_t *result_pool,
12895 apr_pool_t *scratch_pool)
12896 {
12897 svn_boolean_t target_is_wc;
12898 automatic_merge_t *merge;
12899
12900 target_is_wc = (! svn_path_is_url(target_path_or_url))
12901 && (target_revision->kind == svn_opt_revision_unspecified
12902 || target_revision->kind == svn_opt_revision_working
12903 || target_revision->kind == svn_opt_revision_base);
12904 if (target_is_wc)
12905 {
12906 const char *target_abspath;
12907
12908 SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_path_or_url,
12909 scratch_pool));
12910 SVN_ERR(client_find_automatic_merge(
12911 &merge,
12912 source_path_or_url, source_revision,
12913 target_abspath,
12914 TRUE, TRUE, TRUE, /* allow_* */
12915 ctx, scratch_pool, scratch_pool));
12916 }
12917 else
12918 SVN_ERR(find_automatic_merge_no_wc(
12919 &merge,
12920 source_path_or_url, source_revision,
12921 target_path_or_url, target_revision,
12922 ctx, scratch_pool, scratch_pool));
12923
12924 if (needs_reintegration)
12925 *needs_reintegration = merge->is_reintegrate_like;
12926 if (yca_url)
12927 *yca_url = apr_pstrdup(result_pool, merge->yca->url);
12928 if (yca_rev)
12929 *yca_rev = merge->yca->rev;
12930 if (base_url)
12931 *base_url = apr_pstrdup(result_pool, merge->base->url);
12932 if (base_rev)
12933 *base_rev = merge->base->rev;
12934 if (right_url)
12935 *right_url = apr_pstrdup(result_pool, merge->right->url);
12936 if (right_rev)
12937 *right_rev = merge->right->rev;
12938 if (target_url)
12939 *target_url = apr_pstrdup(result_pool, merge->target->url);
12940 if (target_rev)
12941 *target_rev = merge->target->rev;
12942 if (repos_root_url)
12943 *repos_root_url = apr_pstrdup(result_pool, merge->yca->repos_root_url);
12944
12945 return SVN_NO_ERROR;
12946 }
12947