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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
24 /* ==================================================================== */
31 #include <apr_strings.h>
32 #include <apr_tables.h>
34 #include "svn_types.h"
37 #include "svn_delta.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"
47 #include "svn_pools.h"
48 #include "svn_config.h"
49 #include "svn_props.h"
51 #include "svn_sorts.h"
52 #include "svn_subst.h"
55 #include "mergeinfo.h"
57 #include "private/svn_opt_private.h"
58 #include "private/svn_wc_private.h"
59 #include "private/svn_mergeinfo_private.h"
60 #include "private/svn_fspath.h"
61 #include "private/svn_ra_private.h"
62 #include "private/svn_client_private.h"
63 #include "private/svn_subr_private.h"
65 #include "svn_private_config.h"
68 /*-----------------------------------------------------------------------*/
70 /* MERGEINFO MERGE SOURCE NORMALIZATION
72 * Nearly any helper function herein that accepts two URL/revision
73 * pairs (or equivalent struct merge_source_t) expects one of two things
76 * 1. that mergeinfo is not being recorded at all for this
79 * 2. that the pairs represent two locations along a single line
80 * of version history such that there are no copies in the
81 * history of the object between the locations when treating
82 * the oldest of the two locations as non-inclusive. In other
83 * words, if there is a copy at all between them, there is only
84 * one copy and its source was the oldest of the two locations.
86 * We use svn_ra_get_location_segments() to split a given range of
87 * revisions across an object's history into several which obey these
88 * rules. For example, an extract from the log of Subversion's own
89 * /subversion/tags/1.4.5 directory shows the following copies between
90 * r859500 and r866500 (omitting the '/subversion' prefix for clarity):
93 * A /branches/1.4.x (from /trunk:859597)
96 * A /tags/1.4.4 (from /branches/1.4.x:865262)
97 * # Notice that this copy leaves a gap between 865262 and 865417.
100 * A /branches/1.4.5 (from /tags/1.4.4:866419)
104 * A /tags/1.4.5 (from /branches/1.4.5:866424)
108 * 859500 859597 865262 866419 866424 866500
110 * trunk ------------------------------------------------
112 * branches/1.4.x A-------------------------------------
115 * tags/1.4.4 . A-----------------------
117 * branches/1.4.5 . . A------D
119 * tags/1.4.5 . . . A---------
121 * 859598 865417 866420 866425
123 * A merge of the difference between r859500 and r866500 of this directory
124 * gets split into sequential merges of the following location pairs.
126 * 859500 859597 865262 865416 866419 866424 866500
128 * trunk (======] . . . . .
131 * branches/1.4.x ======] . . . .
133 * branches/1.4.x ( . . . .
134 * tags/1.4.4 =============] . .
135 * implicit_src_gap (======] . . .
138 * branches/1.4.5 ======] .
143 * which are represented in merge_source_t as:
145 * [/trunk:859500, /trunk:859597]
146 * (recorded in svn:mergeinfo as /trunk:859501-859597)
148 * [/trunk:859597, /branches/1.4.x:865262]
149 * (recorded in svn:mergeinfo as /branches/1.4.x:859598-865262)
151 * [/branches/1.4.x:865262, /tags/1.4.4@866419]
152 * (recorded in svn:mergeinfo as /tags/1.4.4:865263-866419)
153 * (and there is a gap, the revision range [865262, 865416])
155 * [/tags/1.4.4@866419, /branches/1.4.5@866424]
156 * (recorded in svn:mergeinfo as /branches/1.4.5:866420-866424)
158 * [/branches/1.4.5@866424, /tags/1.4.5@866500]
159 * (recorded in svn:mergeinfo as /tags/1.4.5:866425-866500)
161 * Our helper functions would then operate on one of these location
165 /* WHICH SVN_CLIENT_MERGE* API DO I WANT?
167 * libsvn_client has three public merge APIs; they are all wrappers
168 * around the do_merge engine. Which one to use depends on the number
169 * of URLs passed as arguments and whether or not specific merge
170 * ranges (-c/-r) are specified.
173 * +----+--------------------------------+---------------------+
174 * | -c | mergeinfo-driven | |
175 * | or | cherrypicking | |
176 * | -r | (svn_client_merge_peg) | |
177 * |----+--------------------------------+ |
178 * | | mergeinfo-driven | unsupported |
179 * | | 'cherry harvest', i.e. merge | |
180 * | | all revisions from URL that | |
181 * | no | have not already been merged | |
182 * | -c | (svn_client_merge_peg) | |
183 * | or +--------------------------------+---------------------+
184 * | -r | mergeinfo-driven | mergeinfo-writing |
185 * | | whole-branch | diff-and-apply |
186 * | | heuristic merge | (svn_client_merge) |
187 * | | (svn_client_merge_reintegrate) | |
188 * +----+--------------------------------+---------------------+
193 /* THE CHILDREN_WITH_MERGEINFO ARRAY
195 * Many of the helper functions in this file pass around an
196 * apr_array_header_t *CHILDREN_WITH_MERGEINFO. This is a depth first
197 * sorted array filled with svn_client__merge_path_t * describing the
198 * merge target and any of its subtrees which have explicit mergeinfo
199 * or otherwise need special attention during a merge.
201 * During mergeinfo unaware merges, CHILDREN_WITH_MERGEINFO contains
202 * contains only one element (added by do_mergeinfo_unaware_dir_merge)
203 * describing a contiguous range to be merged to the WC merge target.
205 * During mergeinfo aware merges CHILDREN_WITH_MERGEINFO is created
206 * by get_mergeinfo_paths() and outside of that function and its helpers
207 * should always meet the criteria dictated in get_mergeinfo_paths()'s doc
208 * string. The elements of CHILDREN_WITH_MERGEINFO should never be NULL.
210 * For clarification on mergeinfo aware vs. mergeinfo unaware merges, see
211 * the doc string for HONOR_MERGEINFO().
215 /*-----------------------------------------------------------------------*/
217 /*** Repos-Diff Editor Callbacks ***/
220 typedef struct merge_source_t
222 /* "left" side URL and revision (inclusive iff youngest) */
223 const svn_client__pathrev_t *loc1;
225 /* "right" side URL and revision (inclusive iff youngest) */
226 const svn_client__pathrev_t *loc2;
228 /* True iff LOC1 is an ancestor of LOC2 or vice-versa (history-wise). */
229 svn_boolean_t ancestral;
232 /* Description of the merge target root node (a WC working node) */
233 typedef struct merge_target_t
235 /* Absolute path to the WC node */
238 /* The repository location of the base node of the target WC. If the node
239 * is locally added, then URL & REV are NULL & SVN_INVALID_REVNUM.
240 * REPOS_ROOT_URL and REPOS_UUID are always valid. */
241 svn_client__pathrev_t loc;
245 typedef struct merge_cmd_baton_t {
246 svn_boolean_t force_delete; /* Delete a file/dir even if modified */
247 svn_boolean_t dry_run;
248 svn_boolean_t record_only; /* Whether to merge only mergeinfo
250 svn_boolean_t same_repos; /* Whether the merge source repository
251 is the same repository as the
252 target. Defaults to FALSE if DRY_RUN
254 svn_boolean_t mergeinfo_capable; /* Whether the merge source server
255 is capable of Merge Tracking. */
256 svn_boolean_t ignore_mergeinfo; /* Don't honor mergeinfo; see
257 doc string of do_merge(). FALSE if
258 MERGE_SOURCE->ancestral is FALSE. */
259 svn_boolean_t diff_ignore_ancestry; /* Diff unrelated nodes as if related; see
260 doc string of do_merge(). FALSE if
261 MERGE_SOURCE->ancestral is FALSE. */
262 svn_boolean_t reintegrate_merge; /* Whether this is a --reintegrate
264 const merge_target_t *target; /* Description of merge target node */
266 /* The left and right URLs and revs. The value of this field changes to
267 reflect the merge_source_t *currently* being merged by do_merge(). */
268 merge_source_t merge_source;
270 /* Rangelist containing single range which describes the gap, if any,
271 in the natural history of the merge source currently being processed.
272 See http://subversion.tigris.org/issues/show_bug.cgi?id=3432.
273 Updated during each call to do_directory_merge(). May be NULL if there
275 svn_rangelist_t *implicit_src_gap;
277 svn_client_ctx_t *ctx; /* Client context for callbacks, etc. */
279 /* The list of any paths which remained in conflict after a
280 resolution attempt was made. We track this in-memory, rather
281 than just using WC entry state, since the latter doesn't help us
282 when in dry_run mode.
283 ### And because we only want to resolve conflicts that were
284 generated by this merge, not pre-existing ones? */
285 apr_hash_t *conflicted_paths;
287 /* A list of absolute paths which had no explicit mergeinfo prior to the
288 merge but got explicit mergeinfo added by the merge. This is populated
289 by merge_change_props() 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_new_mergeinfo;
294 /* A list of absolute paths whose mergeinfo doesn't need updating after
295 the merge. This can be caused by the removal of mergeinfo by the merge
296 or by deleting the node itself. This is populated by merge_change_props()
297 and the delete callbacks and is allocated in POOL so it is subject to the
298 lifetime limitations of POOL. Is NULL if no paths are found which
299 meet the criteria or DRY_RUN is true. */
300 apr_hash_t *paths_with_deleted_mergeinfo;
302 /* The list of absolute skipped paths, which should be examined and
303 cleared after each invocation of the callback. The paths
304 are absolute. Is NULL if MERGE_B->MERGE_SOURCE->ancestral and
305 MERGE_B->REINTEGRATE_MERGE are both false. */
306 apr_hash_t *skipped_abspaths;
308 /* The list of absolute merged paths. Unused if MERGE_B->MERGE_SOURCE->ancestral
309 and MERGE_B->REINTEGRATE_MERGE are both false. */
310 apr_hash_t *merged_abspaths;
312 /* A hash of (const char *) absolute WC paths mapped to the same which
313 represent the roots of subtrees added by the merge. */
314 apr_hash_t *added_abspaths;
316 /* A list of tree conflict victim absolute paths which may be NULL. */
317 apr_hash_t *tree_conflicted_abspaths;
319 /* The diff3_cmd in ctx->config, if any, else null. We could just
320 extract this as needed, but since more than one caller uses it,
321 we just set it up when this baton is created. */
322 const char *diff3_cmd;
323 const apr_array_header_t *merge_options;
325 /* Array of file extension patterns to preserve as extensions in
326 generated conflict files. */
327 const apr_array_header_t *ext_patterns;
329 /* RA sessions used throughout a merge operation. Opened/re-parented
332 NOTE: During the actual merge editor drive, RA_SESSION1 is used
333 for the primary editing and RA_SESSION2 for fetching additional
334 information -- as necessary -- from the repository. So during
335 this phase of the merge, you *must not* reparent RA_SESSION1; use
336 (temporarily reparenting if you must) RA_SESSION2 instead. */
337 svn_ra_session_t *ra_session1;
338 svn_ra_session_t *ra_session2;
340 /* During the merge, *USE_SLEEP is set to TRUE if a sleep will be required
341 afterwards to ensure timestamp integrity, or unchanged if not. */
342 svn_boolean_t *use_sleep;
344 /* Pool which has a lifetime limited to one iteration over a given
345 merge source, i.e. it is cleared on every call to do_directory_merge()
346 or do_file_merge() in do_merge(). */
350 /* State for notify_merge_begin() */
351 struct notify_begin_state_t
353 /* Cache of which abspath was last notified. */
354 const char *last_abspath;
356 /* Reference to the one-and-only CHILDREN_WITH_MERGEINFO (see global
357 comment) or a similar list for single-file-merges */
358 const apr_array_header_t *nodes_with_mergeinfo;
364 /* Return TRUE iff we should be taking account of mergeinfo in deciding what
365 changes to merge, for the merge described by MERGE_B. Specifically, that
366 is if the merge source server is capable of merge tracking, the left-side
367 merge source is an ancestor of the right-side (or vice-versa), the merge
368 source is in the same repository as the merge target, and we are not
369 ignoring mergeinfo. */
370 #define HONOR_MERGEINFO(merge_b) ((merge_b)->mergeinfo_capable \
371 && (merge_b)->merge_source.ancestral \
372 && (merge_b)->same_repos \
373 && (! (merge_b)->ignore_mergeinfo))
376 /* Return TRUE iff we should be recording mergeinfo for the merge described
377 by MERGE_B. Specifically, that is if we are honoring mergeinfo and the
378 merge is not a dry run. */
379 #define RECORD_MERGEINFO(merge_b) (HONOR_MERGEINFO(merge_b) \
380 && !(merge_b)->dry_run)
383 /*-----------------------------------------------------------------------*/
387 /* Return TRUE iff the session URL of RA_SESSION is equal to URL. Useful in
388 * asserting preconditions. */
390 session_url_is(svn_ra_session_t *ra_session,
392 apr_pool_t *scratch_pool)
394 const char *session_url;
396 = svn_ra_get_session_url(ra_session, &session_url, scratch_pool);
398 SVN_ERR_ASSERT_NO_RETURN(! err);
399 return strcmp(url, session_url) == 0;
402 /* Return a new merge_source_t structure, allocated in RESULT_POOL,
403 * initialized with deep copies of LOC1 and LOC2 and ANCESTRAL. */
404 static merge_source_t *
405 merge_source_create(const svn_client__pathrev_t *loc1,
406 const svn_client__pathrev_t *loc2,
407 svn_boolean_t ancestral,
408 apr_pool_t *result_pool)
411 = apr_palloc(result_pool, sizeof(*s));
413 s->loc1 = svn_client__pathrev_dup(loc1, result_pool);
414 s->loc2 = svn_client__pathrev_dup(loc2, result_pool);
415 s->ancestral = ancestral;
419 /* Return a deep copy of SOURCE, allocated in RESULT_POOL. */
420 static merge_source_t *
421 merge_source_dup(const merge_source_t *source,
422 apr_pool_t *result_pool)
424 merge_source_t *s = apr_palloc(result_pool, sizeof(*s));
426 s->loc1 = svn_client__pathrev_dup(source->loc1, result_pool);
427 s->loc2 = svn_client__pathrev_dup(source->loc2, result_pool);
428 s->ancestral = source->ancestral;
432 /* Return SVN_ERR_UNSUPPORTED_FEATURE if URL is not inside the repository
433 of LOCAL_ABSPATH. Use SCRATCH_POOL for temporary allocations. */
435 check_repos_match(const merge_target_t *target,
436 const char *local_abspath,
438 apr_pool_t *scratch_pool)
440 if (!svn_uri__is_ancestor(target->loc.repos_root_url, url))
441 return svn_error_createf(
442 SVN_ERR_UNSUPPORTED_FEATURE, NULL,
443 _("URL '%s' of '%s' is not in repository '%s'"),
444 url, svn_dirent_local_style(local_abspath, scratch_pool),
445 target->loc.repos_root_url);
450 /* Return TRUE iff the repository of LOCATION1 is the same as
451 * that of LOCATION2. If STRICT_URLS is true, the URLs must
452 * match (and the UUIDs, just to be sure), otherwise just the UUIDs must
453 * match and the URLs can differ (a common case is http versus https). */
455 is_same_repos(const svn_client__pathrev_t *location1,
456 const svn_client__pathrev_t *location2,
457 svn_boolean_t strict_urls)
460 return (strcmp(location1->repos_root_url, location2->repos_root_url) == 0
461 && strcmp(location1->repos_uuid, location2->repos_uuid) == 0);
463 return (strcmp(location1->repos_uuid, location2->repos_uuid) == 0);
466 /* If the repository identified of LOCATION1 is not the same as that
467 * of LOCATION2, throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES
468 * error mentioning PATH1 and PATH2. For STRICT_URLS, see is_same_repos().
471 check_same_repos(const svn_client__pathrev_t *location1,
473 const svn_client__pathrev_t *location2,
475 svn_boolean_t strict_urls,
476 apr_pool_t *scratch_pool)
478 if (! is_same_repos(location1, location2, strict_urls))
479 return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
480 _("'%s' must be from the same repository as "
481 "'%s'"), path1, path2);
485 /* Store LOCAL_ABSPATH in PATH_HASH after duplicating it into the pool
486 containing PATH_HASH. */
487 static APR_INLINE void
488 store_path(apr_hash_t *path_hash, const char *local_abspath)
490 const char *dup_path = apr_pstrdup(apr_hash_pool_get(path_hash),
493 svn_hash_sets(path_hash, dup_path, dup_path);
496 /* Store LOCAL_ABSPATH in *PATH_HASH_P after duplicating it into the pool
497 containing *PATH_HASH_P. If *PATH_HASH_P is NULL, then first set
498 *PATH_HASH_P to a new hash allocated from POOL. */
499 static APR_INLINE void
500 alloc_and_store_path(apr_hash_t **path_hash_p,
501 const char *local_abspath,
505 *path_hash_p = apr_hash_make(pool);
506 store_path(*path_hash_p, local_abspath);
509 /* Return whether any WC path was put in conflict by the merge
510 operation corresponding to MERGE_B. */
511 static APR_INLINE svn_boolean_t
512 is_path_conflicted_by_merge(merge_cmd_baton_t *merge_b)
514 return (merge_b->conflicted_paths &&
515 apr_hash_count(merge_b->conflicted_paths) > 0);
518 /* Return a state indicating whether the WC metadata matches the
519 * node kind on disk of the local path LOCAL_ABSPATH.
520 * Use MERGE_B to determine the dry-run details; particularly, if a dry run
521 * noted that it deleted this path, assume matching node kinds (as if both
522 * kinds were svn_node_none).
524 * - Return svn_wc_notify_state_inapplicable if the node kind matches.
525 * - Return 'obstructed' if there is a node on disk where none or a
526 * different kind is expected, or if the disk node cannot be read.
527 * - Return 'missing' if there is no node on disk but one is expected.
528 * Also return 'missing' for server-excluded nodes (not here due to
529 * authz or other reasons determined by the server).
531 * Optionally return a bit more info for interested users.
534 perform_obstruction_check(svn_wc_notify_state_t *obstruction_state,
535 svn_boolean_t *deleted,
536 svn_boolean_t *excluded,
537 svn_node_kind_t *kind,
538 svn_depth_t *parent_depth,
539 const merge_cmd_baton_t *merge_b,
540 const char *local_abspath,
541 apr_pool_t *scratch_pool)
543 svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
544 svn_node_kind_t wc_kind;
545 svn_boolean_t check_root;
547 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
549 *obstruction_state = svn_wc_notify_state_inapplicable;
554 *kind = svn_node_none;
559 check_root = ! strcmp(local_abspath, merge_b->target->abspath);
561 SVN_ERR(svn_wc__check_for_obstructions(obstruction_state,
566 wc_ctx, local_abspath,
572 /* Create *LEFT and *RIGHT conflict versions for conflict victim
573 * at VICTIM_ABSPATH, with kind NODE_KIND, using information obtained
574 * from MERGE_SOURCE and TARGET.
575 * Allocate returned conflict versions in RESULT_POOL. */
577 make_conflict_versions(const svn_wc_conflict_version_t **left,
578 const svn_wc_conflict_version_t **right,
579 const char *victim_abspath,
580 svn_node_kind_t node_kind,
581 const merge_source_t *merge_source,
582 const merge_target_t *target,
583 apr_pool_t *result_pool,
584 apr_pool_t *scratch_pool)
586 const char *child = svn_dirent_skip_ancestor(target->abspath,
588 const char *left_relpath, *right_relpath;
590 SVN_ERR_ASSERT(child != NULL);
591 left_relpath = svn_client__pathrev_relpath(merge_source->loc1,
593 right_relpath = svn_client__pathrev_relpath(merge_source->loc2,
596 *left = svn_wc_conflict_version_create2(
597 merge_source->loc1->repos_root_url,
598 merge_source->loc1->repos_uuid,
599 svn_relpath_join(left_relpath, child, scratch_pool),
600 merge_source->loc1->rev, node_kind, result_pool);
602 *right = svn_wc_conflict_version_create2(
603 merge_source->loc2->repos_root_url,
604 merge_source->loc2->repos_uuid,
605 svn_relpath_join(right_relpath, child, scratch_pool),
606 merge_source->loc2->rev, node_kind, result_pool);
611 /* Helper for filter_self_referential_mergeinfo()
613 *MERGEINFO is a non-empty, non-null collection of mergeinfo.
615 Remove all mergeinfo from *MERGEINFO that describes revision ranges
616 greater than REVISION. Put a copy of any removed mergeinfo, allocated
617 in POOL, into *YOUNGER_MERGEINFO.
619 If no mergeinfo is removed from *MERGEINFO then *YOUNGER_MERGEINFO is set
620 to NULL. If all mergeinfo is removed from *MERGEINFO then *MERGEINFO is
624 split_mergeinfo_on_revision(svn_mergeinfo_t *younger_mergeinfo,
625 svn_mergeinfo_t *mergeinfo,
626 svn_revnum_t revision,
629 apr_hash_index_t *hi;
630 apr_pool_t *iterpool = svn_pool_create(pool);
632 *younger_mergeinfo = NULL;
633 for (hi = apr_hash_first(pool, *mergeinfo); hi; hi = apr_hash_next(hi))
636 const char *merge_source_path = svn__apr_hash_index_key(hi);
637 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
639 svn_pool_clear(iterpool);
641 for (i = 0; i < rangelist->nelts; i++)
643 svn_merge_range_t *range =
644 APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
645 if (range->end <= revision)
647 /* This entirely of this range is as old or older than
648 REVISION, so leave it in *MERGEINFO. */
653 /* Since the rangelists in svn_mergeinfo_t's are sorted in
654 increasing order we know that part or all of *this* range
655 and *all* of the remaining ranges in *RANGELIST are younger
656 than REVISION. Remove the younger rangelists from
657 *MERGEINFO and put them in *YOUNGER_MERGEINFO. */
659 svn_rangelist_t *younger_rangelist =
660 apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
662 for (j = i; j < rangelist->nelts; j++)
664 svn_merge_range_t *younger_range = svn_merge_range_dup(
665 APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *), pool);
667 /* REVISION might intersect with the first range where
668 range->end > REVISION. If that is the case then split
669 the current range into two, putting the younger half
670 into *YOUNGER_MERGEINFO and leaving the older half in
672 if (j == i && range->start + 1 <= revision)
673 younger_range->start = range->end = revision;
675 APR_ARRAY_PUSH(younger_rangelist, svn_merge_range_t *) =
679 /* So far we've only been manipulating rangelists, now we
680 actually create *YOUNGER_MERGEINFO and then remove the older
681 ranges from *MERGEINFO */
682 if (!(*younger_mergeinfo))
683 *younger_mergeinfo = apr_hash_make(pool);
684 svn_hash_sets(*younger_mergeinfo, merge_source_path,
686 SVN_ERR(svn_mergeinfo_remove2(mergeinfo, *younger_mergeinfo,
687 *mergeinfo, TRUE, pool, iterpool));
688 break; /* ...out of for (i = 0; i < rangelist->nelts; i++) */
693 svn_pool_destroy(iterpool);
699 /* Make a copy of PROPCHANGES (array of svn_prop_t) into *TRIMMED_PROPCHANGES,
700 omitting any svn:mergeinfo changes. */
702 omit_mergeinfo_changes(apr_array_header_t **trimmed_propchanges,
703 const apr_array_header_t *propchanges,
704 apr_pool_t *result_pool)
708 *trimmed_propchanges = apr_array_make(result_pool,
712 for (i = 0; i < propchanges->nelts; ++i)
714 const svn_prop_t *change = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
716 /* If this property is not svn:mergeinfo, then copy it. */
717 if (strcmp(change->name, SVN_PROP_MERGEINFO) != 0)
718 APR_ARRAY_PUSH(*trimmed_propchanges, svn_prop_t) = *change;
725 /* Helper for merge_props_changed().
727 *PROPS is an array of svn_prop_t structures representing regular properties
728 to be added to the working copy TARGET_ABSPATH.
730 The merge source and target are assumed to be in the same repository.
732 Filter out mergeinfo property additions to TARGET_ABSPATH when
733 those additions refer to the same line of history as TARGET_ABSPATH as
736 Examine the added mergeinfo, looking at each range (or single rev)
737 of each source path. If a source_path/range refers to the same line of
738 history as TARGET_ABSPATH (pegged at its base revision), then filter out
739 that range. If the entire rangelist for a given path is filtered then
740 filter out the path as well.
742 RA_SESSION is an open RA session to the repository
743 in which both the source and target live, else RA_SESSION is not used. It
744 may be temporarily reparented as needed by this function.
746 Use CTX for any further client operations.
748 If any filtering occurs, set outgoing *PROPS to a shallow copy (allocated
749 in POOL) of incoming *PROPS minus the filtered mergeinfo. */
751 filter_self_referential_mergeinfo(apr_array_header_t **props,
752 const char *target_abspath,
753 svn_ra_session_t *ra_session,
754 svn_client_ctx_t *ctx,
757 apr_array_header_t *adjusted_props;
759 apr_pool_t *iterpool;
760 svn_boolean_t is_copy;
761 const char *repos_relpath;
762 svn_client__pathrev_t target_base;
764 /* If PATH itself has been added there is no need to filter. */
765 SVN_ERR(svn_wc__node_get_origin(&is_copy, &target_base.rev, &repos_relpath,
766 &target_base.repos_root_url,
767 &target_base.repos_uuid, NULL,
768 ctx->wc_ctx, target_abspath, FALSE,
771 if (is_copy || !repos_relpath)
772 return SVN_NO_ERROR; /* A copy or a local addition */
774 target_base.url = svn_path_url_add_component2(target_base.repos_root_url,
775 repos_relpath, pool);
777 adjusted_props = apr_array_make(pool, (*props)->nelts, sizeof(svn_prop_t));
778 iterpool = svn_pool_create(pool);
779 for (i = 0; i < (*props)->nelts; ++i)
781 svn_prop_t *prop = &APR_ARRAY_IDX((*props), i, svn_prop_t);
783 svn_mergeinfo_t mergeinfo, younger_mergeinfo;
784 svn_mergeinfo_t filtered_mergeinfo = NULL;
785 svn_mergeinfo_t filtered_younger_mergeinfo = NULL;
788 /* If this property isn't mergeinfo or is NULL valued (i.e. prop removal)
789 or empty mergeinfo it does not require any special handling. There
790 is nothing to filter out of empty mergeinfo and the concept of
791 filtering doesn't apply if we are trying to remove mergeinfo
793 if ((strcmp(prop->name, SVN_PROP_MERGEINFO) != 0)
794 || (! prop->value) /* Removal of mergeinfo */
795 || (! prop->value->len)) /* Empty mergeinfo */
797 APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
801 svn_pool_clear(iterpool);
803 /* Non-empty mergeinfo; filter self-referential mergeinfo out. */
805 /* Parse the incoming mergeinfo to allow easier manipulation. */
806 err = svn_mergeinfo_parse(&mergeinfo, prop->value->data, iterpool);
810 /* Issue #3896: If we can't parse it, we certainly can't
812 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
814 svn_error_clear(err);
815 APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
820 return svn_error_trace(err);
824 /* The working copy target PATH is at BASE_REVISION. Divide the
825 incoming mergeinfo into two groups. One where all revision ranges
826 are as old or older than BASE_REVISION and one where all revision
829 Note: You may be wondering why we do this.
831 For the incoming mergeinfo "older" than target's base revision we
832 can filter out self-referential mergeinfo efficiently using
833 svn_client__get_history_as_mergeinfo(). We simply look at PATH's
834 natural history as mergeinfo and remove that from any incoming
837 For mergeinfo "younger" than the base revision we can't use
838 svn_ra_get_location_segments() to look into PATH's future
839 history. Instead we must use svn_client__repos_locations() and
840 look at each incoming source/range individually and see if PATH
841 at its base revision and PATH at the start of the incoming range
842 exist on the same line of history. If they do then we can filter
843 out the incoming range. But since we have to do this for each
844 range there is a substantial performance penalty to pay if the
845 incoming ranges are not contiguous, i.e. we call
846 svn_client__repos_locations for each discrete range and incur
847 the cost of a roundtrip communication with the repository. */
848 SVN_ERR(split_mergeinfo_on_revision(&younger_mergeinfo,
853 /* Filter self-referential mergeinfo from younger_mergeinfo. */
854 if (younger_mergeinfo)
856 apr_hash_index_t *hi;
857 const char *merge_source_root_url;
859 SVN_ERR(svn_ra_get_repos_root2(ra_session,
860 &merge_source_root_url, iterpool));
862 for (hi = apr_hash_first(iterpool, younger_mergeinfo);
863 hi; hi = apr_hash_next(hi))
866 const char *source_path = svn__apr_hash_index_key(hi);
867 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
868 const char *merge_source_url;
869 svn_rangelist_t *adjusted_rangelist =
870 apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
873 svn_path_url_add_component2(merge_source_root_url,
874 source_path + 1, iterpool);
876 for (j = 0; j < rangelist->nelts; j++)
879 svn_client__pathrev_t *start_loc;
880 svn_merge_range_t *range =
881 APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *);
883 /* Because the merge source normalization code
884 ensures mergeinfo refers to real locations on
885 the same line of history, there's no need to
886 look at the whole range, just the start. */
888 /* Check if PATH@BASE_REVISION exists at
889 RANGE->START on the same line of history.
890 (start+1 because RANGE->start is not inclusive.) */
891 err2 = svn_client__repos_location(&start_loc, ra_session,
894 ctx, iterpool, iterpool);
897 if (err2->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES
898 || err2->apr_err == SVN_ERR_FS_NOT_FOUND
899 || err2->apr_err == SVN_ERR_FS_NO_SUCH_REVISION)
901 /* PATH@BASE_REVISION didn't exist at
902 RANGE->START + 1 or is unrelated to the
903 resource PATH@RANGE->START. Some of the
904 requested revisions may not even exist in
905 the repository; a real possibility since
906 mergeinfo is hand editable. In all of these
907 cases clear and ignore the error and don't
910 Note: In this last case it is possible that
911 we will allow self-referential mergeinfo to
912 be applied, but fixing it here is potentially
913 very costly in terms of finding what part of
914 a range is actually valid. Simply allowing
915 the merge to proceed without filtering the
916 offending range seems the least worst
918 svn_error_clear(err2);
920 APR_ARRAY_PUSH(adjusted_rangelist,
921 svn_merge_range_t *) = range;
925 return svn_error_trace(err2);
930 /* PATH@BASE_REVISION exists on the same
931 line of history at RANGE->START and RANGE->END.
932 Now check that PATH@BASE_REVISION's path
933 names at RANGE->START and RANGE->END are the same.
934 If the names are not the same then the mergeinfo
935 describing PATH@RANGE->START through
936 PATH@RANGE->END actually belong to some other
937 line of history and we want to record this
938 mergeinfo, not filter it. */
939 if (strcmp(start_loc->url, merge_source_url) != 0)
941 APR_ARRAY_PUSH(adjusted_rangelist,
942 svn_merge_range_t *) = range;
945 /* else no need to add, this mergeinfo is
946 all on the same line of history. */
947 } /* for (j = 0; j < rangelist->nelts; j++) */
949 /* Add any rangelists for source_path that are not
951 if (adjusted_rangelist->nelts)
953 if (!filtered_younger_mergeinfo)
954 filtered_younger_mergeinfo = apr_hash_make(iterpool);
955 svn_hash_sets(filtered_younger_mergeinfo, source_path,
959 } /* Iteration over each merge source in younger_mergeinfo. */
960 } /* if (younger_mergeinfo) */
962 /* Filter self-referential mergeinfo from "older" mergeinfo. */
965 svn_mergeinfo_t implicit_mergeinfo;
967 SVN_ERR(svn_client__get_history_as_mergeinfo(
968 &implicit_mergeinfo, NULL,
969 &target_base, target_base.rev, SVN_INVALID_REVNUM,
970 ra_session, ctx, iterpool));
972 /* Remove PATH's implicit mergeinfo from the incoming mergeinfo. */
973 SVN_ERR(svn_mergeinfo_remove2(&filtered_mergeinfo,
975 mergeinfo, TRUE, iterpool, iterpool));
978 /* Combine whatever older and younger filtered mergeinfo exists
979 into filtered_mergeinfo. */
980 if (filtered_mergeinfo && filtered_younger_mergeinfo)
981 SVN_ERR(svn_mergeinfo_merge2(filtered_mergeinfo,
982 filtered_younger_mergeinfo, iterpool,
984 else if (filtered_younger_mergeinfo)
985 filtered_mergeinfo = filtered_younger_mergeinfo;
987 /* If there is any incoming mergeinfo remaining after filtering
988 then put it in adjusted_props. */
989 if (filtered_mergeinfo && apr_hash_count(filtered_mergeinfo))
991 /* Convert filtered_mergeinfo to a svn_prop_t and put it
992 back in the array. */
993 svn_string_t *filtered_mergeinfo_str;
994 svn_prop_t *adjusted_prop = apr_pcalloc(pool,
995 sizeof(*adjusted_prop));
996 SVN_ERR(svn_mergeinfo_to_string(&filtered_mergeinfo_str,
999 adjusted_prop->name = SVN_PROP_MERGEINFO;
1000 adjusted_prop->value = filtered_mergeinfo_str;
1001 APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *adjusted_prop;
1004 svn_pool_destroy(iterpool);
1006 *props = adjusted_props;
1007 return SVN_NO_ERROR;
1010 /* Prepare a set of property changes PROPCHANGES to be used for a merge
1011 operation on LOCAL_ABSPATH.
1013 Remove all non-regular prop-changes (entry-props and WC-props).
1014 Remove all non-mergeinfo prop-changes if it's a record-only merge.
1015 Remove self-referential mergeinfo (### in some cases...)
1016 Remove foreign-repository mergeinfo (### in some cases...)
1018 Store the resulting property changes in *PROP_UPDATES.
1019 Store information on where mergeinfo is updated in MERGE_B.
1021 Used for both file and directory property merges. */
1022 static svn_error_t *
1023 prepare_merge_props_changed(const apr_array_header_t **prop_updates,
1024 const char *local_abspath,
1025 const apr_array_header_t *propchanges,
1026 merge_cmd_baton_t *merge_b,
1027 apr_pool_t *result_pool,
1028 apr_pool_t *scratch_pool)
1030 apr_array_header_t *props;
1032 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1034 /* We only want to merge "regular" version properties: by
1035 definition, 'svn merge' shouldn't touch any data within .svn/ */
1036 SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
1039 /* If we are only applying mergeinfo changes then we need to do
1040 additional filtering of PROPS so it contains only mergeinfo changes. */
1041 if (merge_b->record_only && props->nelts)
1043 apr_array_header_t *mergeinfo_props =
1044 apr_array_make(result_pool, 1, sizeof(svn_prop_t));
1047 for (i = 0; i < props->nelts; i++)
1049 svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
1051 if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
1053 APR_ARRAY_PUSH(mergeinfo_props, svn_prop_t) = *prop;
1057 props = mergeinfo_props;
1062 /* Issue #3383: We don't want mergeinfo from a foreign repos.
1064 If this is a merge from a foreign repository we must strip all
1065 incoming mergeinfo (including mergeinfo deletions). */
1066 if (! merge_b->same_repos)
1067 SVN_ERR(omit_mergeinfo_changes(&props, props, result_pool));
1069 /* If this is a forward merge then don't add new mergeinfo to
1070 PATH that is already part of PATH's own history, see
1071 http://svn.haxx.se/dev/archive-2008-09/0006.shtml. If the
1072 merge sources are not ancestral then there is no concept of a
1073 'forward' or 'reverse' merge and we filter unconditionally. */
1074 if (merge_b->merge_source.loc1->rev < merge_b->merge_source.loc2->rev
1075 || !merge_b->merge_source.ancestral)
1077 if (HONOR_MERGEINFO(merge_b) || merge_b->reintegrate_merge)
1078 SVN_ERR(filter_self_referential_mergeinfo(&props,
1080 merge_b->ra_session2,
1085 *prop_updates = props;
1087 /* Make a record in BATON if we find a PATH where mergeinfo is added
1088 where none existed previously or PATH is having its existing
1089 mergeinfo deleted. */
1094 for (i = 0; i < props->nelts; ++i)
1096 svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
1098 if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
1100 /* Does LOCAL_ABSPATH have any pristine mergeinfo? */
1101 svn_boolean_t has_pristine_mergeinfo = FALSE;
1102 apr_hash_t *pristine_props;
1104 SVN_ERR(svn_wc_get_pristine_props(&pristine_props,
1105 merge_b->ctx->wc_ctx,
1111 && svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
1112 has_pristine_mergeinfo = TRUE;
1114 if (!has_pristine_mergeinfo && prop->value)
1116 alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
1117 local_abspath, merge_b->pool);
1119 else if (has_pristine_mergeinfo && !prop->value)
1121 alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
1122 local_abspath, merge_b->pool);
1128 return SVN_NO_ERROR;
1131 #define CONFLICT_REASON_NONE ((svn_wc_conflict_reason_t)-1)
1132 #define CONFLICT_REASON_SKIP ((svn_wc_conflict_reason_t)-2)
1133 #define CONFLICT_REASON_SKIP_WC ((svn_wc_conflict_reason_t)-3)
1135 /* Baton used for testing trees for being editted while performing tree
1136 conflict detection for incoming deletes */
1137 struct dir_delete_baton_t
1139 /* Reference to dir baton of directory that is the root of the deletion */
1140 struct merge_dir_baton_t *del_root;
1142 /* Boolean indicating that some edit is found. Allows avoiding more work */
1143 svn_boolean_t found_edit;
1145 /* A list of paths that are compared. Kept up to date until FOUND_EDIT is
1147 apr_hash_t *compared_abspaths;
1150 /* Baton for the merge_dir_*() functions. Initialized in merge_dir_opened() */
1151 struct merge_dir_baton_t
1153 /* Reference to the parent baton, unless the parent is the anchor, in which
1154 case PARENT_BATON is NULL */
1155 struct merge_dir_baton_t *parent_baton;
1157 /* The pool containing this baton. Use for RESULT_POOL for storing in this
1161 /* This directory doesn't have a representation in the working copy, so any
1162 operation on it will be skipped and possibly cause a tree conflict on the
1164 svn_boolean_t shadowed;
1166 /* This node or one of its descendants received operational changes from the
1167 merge. If this node is the shadow root its tree conflict status has been
1169 svn_boolean_t edited;
1171 /* If a tree conflict will be installed once edited, it's reason. If a skip
1172 should be produced its reason. Otherwise CONFLICT_REASON_NONE for no tree
1176 CONFLICT_REASON_SKIP:
1177 The node will be skipped with content and property state as stored in
1180 CONFLICT_REASON_SKIP_WC:
1181 The node will be skipped as an obstructing working copy.
1183 svn_wc_conflict_reason_t tree_conflict_reason;
1184 svn_wc_conflict_action_t tree_conflict_action;
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;
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 */
1194 /* TRUE if we are taking over an existing directory as addition, otherwise
1196 svn_boolean_t add_existing;
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
1202 apr_hash_t *pending_deletes;
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;
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;
1215 /* Baton for the merge_dir_*() functions. Initialized in merge_file_opened() */
1216 struct merge_file_baton_t
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;
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;
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;
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_tree_baton_t for an explanation. */
1234 svn_wc_conflict_reason_t tree_conflict_reason;
1235 svn_wc_conflict_action_t tree_conflict_action;
1237 /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
1238 add to the notification */
1239 svn_wc_notify_state_t skip_reason;
1241 /* TRUE if the node was added by this merge. Otherwise FALSE */
1242 svn_boolean_t added;
1243 svn_boolean_t add_is_replace; /* Add is second part of replace */
1246 /* Forward declaration */
1247 static svn_error_t *
1248 notify_merge_begin(merge_cmd_baton_t *merge_b,
1249 const char *local_abspath,
1250 svn_boolean_t delete_action,
1251 apr_pool_t *scratch_pool);
1253 /* Record the skip for future processing and (later) produce the
1254 skip notification */
1255 static svn_error_t *
1256 record_skip(merge_cmd_baton_t *merge_b,
1257 const char *local_abspath,
1258 svn_node_kind_t kind,
1259 svn_wc_notify_action_t action,
1260 svn_wc_notify_state_t state,
1261 struct merge_dir_baton_t *pdb,
1262 apr_pool_t *scratch_pool)
1264 if (merge_b->record_only)
1265 return SVN_NO_ERROR; /* ### Why? - Legacy compatibility */
1267 if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
1268 && !(pdb && pdb->shadowed))
1270 store_path(merge_b->skipped_abspaths, local_abspath);
1273 if (merge_b->ctx->notify_func2)
1275 svn_wc_notify_t *notify;
1277 SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
1279 notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
1280 notify->kind = kind;
1281 notify->content_state = notify->prop_state = state;
1283 (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
1286 return SVN_NO_ERROR;
1289 /* Record a tree conflict in the WC, unless this is a dry run or a record-
1290 * only merge, or if a tree conflict is already flagged for the VICTIM_PATH.
1291 * (The latter can happen if a merge-tracking-aware merge is doing multiple
1292 * editor drives because of a gap in the range of eligible revisions.)
1294 * The tree conflict, with its victim specified by VICTIM_PATH, is
1295 * assumed to have happened during a merge using merge baton MERGE_B.
1297 * NODE_KIND must be the node kind of "old" and "theirs" and "mine";
1298 * this function cannot cope with node kind clashes.
1299 * ACTION and REASON correspond to the fields
1300 * of the same names in svn_wc_tree_conflict_description_t.
1302 static svn_error_t *
1303 record_tree_conflict(merge_cmd_baton_t *merge_b,
1304 const char *local_abspath,
1305 struct merge_dir_baton_t *parent_baton,
1306 svn_node_kind_t node_kind,
1307 svn_wc_conflict_action_t action,
1308 svn_wc_conflict_reason_t reason,
1309 const svn_wc_conflict_description2_t *existing_conflict,
1310 svn_boolean_t notify_tc,
1311 apr_pool_t *scratch_pool)
1313 svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
1315 if (merge_b->record_only)
1316 return SVN_NO_ERROR;
1318 if (merge_b->merge_source.ancestral
1319 || merge_b->reintegrate_merge)
1321 store_path(merge_b->tree_conflicted_abspaths, local_abspath);
1324 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
1327 if (!merge_b->dry_run)
1329 svn_wc_conflict_description2_t *conflict;
1330 const svn_wc_conflict_version_t *left;
1331 const svn_wc_conflict_version_t *right;
1332 apr_pool_t *result_pool = parent_baton ? parent_baton->pool
1335 if (reason == svn_wc_conflict_reason_deleted)
1337 const char *moved_to_abspath;
1339 SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
1340 wc_ctx, local_abspath,
1341 scratch_pool, scratch_pool));
1343 if (moved_to_abspath)
1345 /* Local abspath itself has been moved away. If only a
1346 descendant is moved away, we call the node itself deleted */
1347 reason = svn_wc_conflict_reason_moved_away;
1350 else if (reason == svn_wc_conflict_reason_added)
1352 const char *moved_from_abspath;
1353 SVN_ERR(svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
1354 wc_ctx, local_abspath,
1355 scratch_pool, scratch_pool));
1356 if (moved_from_abspath)
1357 reason = svn_wc_conflict_reason_moved_here;
1360 SVN_ERR(make_conflict_versions(&left, &right, local_abspath, node_kind,
1361 &merge_b->merge_source, merge_b->target,
1362 result_pool, scratch_pool));
1364 /* Fix up delete of file, add of dir replacement (or other way around) */
1365 if (existing_conflict != NULL && existing_conflict->src_left_version)
1366 left = existing_conflict->src_left_version;
1368 conflict = svn_wc_conflict_description_create_tree2(
1369 local_abspath, node_kind, svn_wc_operation_merge,
1370 left, right, result_pool);
1372 conflict->action = action;
1373 conflict->reason = reason;
1375 /* May return SVN_ERR_WC_PATH_UNEXPECTED_STATUS */
1376 if (existing_conflict)
1377 SVN_ERR(svn_wc__del_tree_conflict(wc_ctx, local_abspath,
1380 SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
1385 if (! parent_baton->new_tree_conflicts)
1386 parent_baton->new_tree_conflicts = apr_hash_make(result_pool);
1388 svn_hash_sets(parent_baton->new_tree_conflicts,
1389 apr_pstrdup(result_pool, local_abspath),
1393 /* ### TODO: Store in parent baton */
1396 /* On a replacement we currently get two tree conflicts */
1397 if (merge_b->ctx->notify_func2 && notify_tc)
1399 svn_wc_notify_t *notify;
1401 SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
1403 notify = svn_wc_create_notify(local_abspath, svn_wc_notify_tree_conflict,
1405 notify->kind = node_kind;
1407 (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
1411 return SVN_NO_ERROR;
1414 /* Record the add for future processing and produce the
1415 update_add notification
1417 static svn_error_t *
1418 record_update_add(merge_cmd_baton_t *merge_b,
1419 const char *local_abspath,
1420 svn_node_kind_t kind,
1421 svn_boolean_t notify_replaced,
1422 apr_pool_t *scratch_pool)
1424 if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
1426 store_path(merge_b->merged_abspaths, local_abspath);
1429 if (merge_b->ctx->notify_func2)
1431 svn_wc_notify_t *notify;
1432 svn_wc_notify_action_t action = svn_wc_notify_update_add;
1434 SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
1436 if (notify_replaced)
1437 action = svn_wc_notify_update_replace;
1439 notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
1440 notify->kind = kind;
1442 (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
1446 return SVN_NO_ERROR;
1449 /* Record the update for future processing and produce the
1450 update_update notification */
1451 static svn_error_t *
1452 record_update_update(merge_cmd_baton_t *merge_b,
1453 const char *local_abspath,
1454 svn_node_kind_t kind,
1455 svn_wc_notify_state_t content_state,
1456 svn_wc_notify_state_t prop_state,
1457 apr_pool_t *scratch_pool)
1459 if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
1461 store_path(merge_b->merged_abspaths, local_abspath);
1464 if (merge_b->ctx->notify_func2)
1466 svn_wc_notify_t *notify;
1468 SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
1470 notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_update,
1472 notify->kind = kind;
1473 notify->content_state = content_state;
1474 notify->prop_state = prop_state;
1476 (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
1480 return SVN_NO_ERROR;
1483 /* Record the delete for future processing and for (later) producing the
1484 update_delete notification */
1485 static svn_error_t *
1486 record_update_delete(merge_cmd_baton_t *merge_b,
1487 struct merge_dir_baton_t *parent_db,
1488 const char *local_abspath,
1489 svn_node_kind_t kind,
1490 apr_pool_t *scratch_pool)
1492 /* Update the lists of merged, skipped, tree-conflicted and added paths. */
1493 if (merge_b->merge_source.ancestral
1494 || merge_b->reintegrate_merge)
1496 /* Issue #4166: If a previous merge added NOTIFY_ABSPATH, but we
1497 are now deleting it, then remove it from the list of added
1499 svn_hash_sets(merge_b->added_abspaths, local_abspath, NULL);
1500 store_path(merge_b->merged_abspaths, local_abspath);
1503 SVN_ERR(notify_merge_begin(merge_b, local_abspath, TRUE, scratch_pool));
1507 const char *dup_abspath = apr_pstrdup(parent_db->pool, local_abspath);
1509 if (!parent_db->pending_deletes)
1510 parent_db->pending_deletes = apr_hash_make(parent_db->pool);
1512 svn_hash_sets(parent_db->pending_deletes, dup_abspath,
1513 svn_node_kind_to_word(kind));
1516 return SVN_NO_ERROR;
1519 /* Notify the pending 'D'eletes, that were waiting to see if a matching 'A'dd
1520 might make them a 'R'eplace. */
1521 static svn_error_t *
1522 handle_pending_notifications(merge_cmd_baton_t *merge_b,
1523 struct merge_dir_baton_t *db,
1524 apr_pool_t *scratch_pool)
1526 if (merge_b->ctx->notify_func2 && db->pending_deletes)
1528 apr_hash_index_t *hi;
1530 for (hi = apr_hash_first(scratch_pool, db->pending_deletes);
1532 hi = apr_hash_next(hi))
1534 const char *del_abspath = svn__apr_hash_index_key(hi);
1535 svn_wc_notify_t *notify;
1537 notify = svn_wc_create_notify(del_abspath,
1538 svn_wc_notify_update_delete,
1540 notify->kind = svn_node_kind_from_word(
1541 svn__apr_hash_index_val(hi));
1543 (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
1544 notify, scratch_pool);
1547 db->pending_deletes = NULL;
1549 return SVN_NO_ERROR;
1552 /* Helper function for the merge_dir_*() and merge_file_*() functions.
1554 Installs and notifies pre-recorded tree conflicts and skips for
1555 ancestors of operational merges
1557 static svn_error_t *
1558 mark_dir_edited(merge_cmd_baton_t *merge_b,
1559 struct merge_dir_baton_t *db,
1560 const char *local_abspath,
1561 apr_pool_t *scratch_pool)
1563 /* ### Too much common code with mark_file_edited */
1565 return SVN_NO_ERROR;
1567 if (db->parent_baton && !db->parent_baton->edited)
1569 const char *dir_abspath = svn_dirent_dirname(local_abspath,
1572 SVN_ERR(mark_dir_edited(merge_b, db->parent_baton, dir_abspath,
1579 return SVN_NO_ERROR; /* Easy out */
1581 if (db->parent_baton
1582 && db->parent_baton->delete_state
1583 && db->tree_conflict_reason != CONFLICT_REASON_NONE)
1585 db->parent_baton->delete_state->found_edit = TRUE;
1587 else if (db->tree_conflict_reason == CONFLICT_REASON_SKIP
1588 || db->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
1590 /* open_directory() decided not to flag a tree conflict, but
1591 for clarity we produce a skip for this node that
1592 most likely isn't touched by the merge itself */
1594 if (merge_b->ctx->notify_func2)
1596 svn_wc_notify_t *notify;
1598 SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE,
1601 notify = svn_wc_create_notify(
1603 (db->tree_conflict_reason == CONFLICT_REASON_SKIP)
1604 ? svn_wc_notify_skip
1605 : svn_wc_notify_update_skip_obstruction,
1607 notify->kind = svn_node_dir;
1608 notify->content_state = notify->prop_state = db->skip_reason;
1610 (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
1615 if (merge_b->merge_source.ancestral
1616 || merge_b->reintegrate_merge)
1618 store_path(merge_b->skipped_abspaths, local_abspath);
1621 else if (db->tree_conflict_reason != CONFLICT_REASON_NONE)
1623 /* open_directory() decided that a tree conflict should be raised */
1625 SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
1626 svn_node_dir, db->tree_conflict_action,
1627 db->tree_conflict_reason,
1632 return SVN_NO_ERROR;
1635 /* Helper function for the merge_file_*() functions.
1637 Installs and notifies pre-recorded tree conflicts and skips for
1638 ancestors of operational merges
1640 static svn_error_t *
1641 mark_file_edited(merge_cmd_baton_t *merge_b,
1642 struct merge_file_baton_t *fb,
1643 const char *local_abspath,
1644 apr_pool_t *scratch_pool)
1646 /* ### Too much common code with mark_dir_edited */
1648 return SVN_NO_ERROR;
1650 if (fb->parent_baton && !fb->parent_baton->edited)
1652 const char *dir_abspath = svn_dirent_dirname(local_abspath,
1655 SVN_ERR(mark_dir_edited(merge_b, fb->parent_baton, dir_abspath,
1662 return SVN_NO_ERROR; /* Easy out */
1664 if (fb->parent_baton
1665 && fb->parent_baton->delete_state
1666 && fb->tree_conflict_reason != CONFLICT_REASON_NONE)
1668 fb->parent_baton->delete_state->found_edit = TRUE;
1670 else if (fb->tree_conflict_reason == CONFLICT_REASON_SKIP
1671 || fb->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
1673 /* open_directory() decided not to flag a tree conflict, but
1674 for clarity we produce a skip for this node that
1675 most likely isn't touched by the merge itself */
1677 if (merge_b->ctx->notify_func2)
1679 svn_wc_notify_t *notify;
1681 SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE,
1684 notify = svn_wc_create_notify(local_abspath, svn_wc_notify_skip,
1686 notify->kind = svn_node_file;
1687 notify->content_state = notify->prop_state = fb->skip_reason;
1689 (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
1694 if (merge_b->merge_source.ancestral
1695 || merge_b->reintegrate_merge)
1697 store_path(merge_b->skipped_abspaths, local_abspath);
1700 else if (fb->tree_conflict_reason != CONFLICT_REASON_NONE)
1702 /* open_file() decided that a tree conflict should be raised */
1704 SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
1705 svn_node_file, fb->tree_conflict_action,
1706 fb->tree_conflict_reason,
1711 return SVN_NO_ERROR;
1714 /* An svn_diff_tree_processor_t function.
1716 Called before either merge_file_changed(), merge_file_added(),
1717 merge_file_deleted() or merge_file_closed(), unless it sets *SKIP to TRUE.
1719 When *SKIP is TRUE, the diff driver avoids work on getting the details
1720 for the closing callbacks.
1722 static svn_error_t *
1723 merge_file_opened(void **new_file_baton,
1724 svn_boolean_t *skip,
1725 const char *relpath,
1726 const svn_diff_source_t *left_source,
1727 const svn_diff_source_t *right_source,
1728 const svn_diff_source_t *copyfrom_source,
1730 const struct svn_diff_tree_processor_t *processor,
1731 apr_pool_t *result_pool,
1732 apr_pool_t *scratch_pool)
1734 merge_cmd_baton_t *merge_b = processor->baton;
1735 struct merge_dir_baton_t *pdb = dir_baton;
1736 struct merge_file_baton_t *fb;
1737 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
1738 relpath, scratch_pool);
1740 fb = apr_pcalloc(result_pool, sizeof(*fb));
1741 fb->tree_conflict_reason = CONFLICT_REASON_NONE;
1742 fb->tree_conflict_action = svn_wc_conflict_action_edit;
1743 fb->skip_reason = svn_wc_notify_state_unknown;
1745 *new_file_baton = fb;
1749 fb->parent_baton = pdb;
1750 fb->shadowed = pdb->shadowed;
1751 fb->skip_reason = pdb->skip_reason;
1756 /* An ancestor is tree conflicted. Nothing to do here. */
1758 else if (left_source != NULL)
1760 /* Node is expected to be a file, which will be changed or deleted. */
1761 svn_node_kind_t kind;
1762 svn_boolean_t is_deleted;
1763 svn_boolean_t excluded;
1764 svn_depth_t parent_depth;
1767 fb->tree_conflict_action = svn_wc_conflict_action_delete;
1770 svn_wc_notify_state_t obstr_state;
1772 SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
1773 &kind, &parent_depth,
1774 merge_b, local_abspath,
1777 if (obstr_state != svn_wc_notify_state_inapplicable)
1779 fb->shadowed = TRUE;
1780 fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1781 fb->skip_reason = obstr_state;
1782 return SVN_NO_ERROR;
1786 kind = svn_node_none;
1789 if (kind == svn_node_none)
1791 fb->shadowed = TRUE;
1793 /* If this is not the merge target and the parent is too shallow to
1794 contain this directory, and the directory is not present
1795 via exclusion or depth filtering, skip it instead of recording
1798 Non-inheritable mergeinfo will be recorded, allowing
1799 future merges into non-shallow working copies to merge
1800 changes we missed this time around. */
1801 if (pdb && (excluded
1802 || (parent_depth != svn_depth_unknown &&
1803 parent_depth < svn_depth_files)))
1805 fb->shadowed = TRUE;
1807 fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1808 fb->skip_reason = svn_wc_notify_state_missing;
1809 return SVN_NO_ERROR;
1813 fb->tree_conflict_reason = svn_wc_conflict_reason_deleted;
1815 fb->tree_conflict_reason = svn_wc_conflict_reason_missing;
1817 /* ### Similar to directory */
1819 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1820 return SVN_NO_ERROR;
1823 else if (kind != svn_node_file)
1825 fb->shadowed = TRUE;
1827 fb->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
1829 /* ### Similar to directory */
1831 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1832 return SVN_NO_ERROR;
1838 /* We want to delete the directory */
1839 fb->tree_conflict_action = svn_wc_conflict_action_delete;
1840 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1844 return SVN_NO_ERROR; /* Already set a tree conflict */
1847 /* Comparison mode to verify for delete tree conflicts? */
1848 if (pdb && pdb->delete_state
1849 && pdb->delete_state->found_edit)
1851 /* Earlier nodes found a conflict. Done. */
1858 const svn_wc_conflict_description2_t *old_tc = NULL;
1860 /* The node doesn't exist pre-merge: We have an addition */
1862 fb->tree_conflict_action = svn_wc_conflict_action_add;
1864 if (pdb && pdb->pending_deletes
1865 && svn_hash_gets(pdb->pending_deletes, local_abspath))
1867 fb->add_is_replace = TRUE;
1868 fb->tree_conflict_action = svn_wc_conflict_action_replace;
1870 svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
1874 && pdb->new_tree_conflicts
1875 && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
1877 fb->tree_conflict_action = svn_wc_conflict_action_replace;
1878 fb->tree_conflict_reason = old_tc->reason;
1880 /* Update the tree conflict to store that this is a replace */
1881 SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
1883 fb->tree_conflict_action,
1884 fb->tree_conflict_reason,
1888 if (old_tc->reason == svn_wc_conflict_reason_deleted
1889 || old_tc->reason == svn_wc_conflict_reason_moved_away)
1891 /* Issue #3806: Incoming replacements on local deletes produce
1892 inconsistent result.
1894 In this specific case we can continue applying the add part
1895 of the replacement. */
1901 return SVN_NO_ERROR;
1904 else if (! (merge_b->dry_run
1905 && ((pdb && pdb->added) || fb->add_is_replace)))
1907 svn_wc_notify_state_t obstr_state;
1908 svn_node_kind_t kind;
1909 svn_boolean_t is_deleted;
1911 SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
1913 merge_b, local_abspath,
1916 if (obstr_state != svn_wc_notify_state_inapplicable)
1918 /* Skip the obstruction */
1919 fb->shadowed = TRUE;
1920 fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1921 fb->skip_reason = obstr_state;
1923 else if (kind != svn_node_none && !is_deleted)
1925 /* Set a tree conflict */
1926 fb->shadowed = TRUE;
1927 fb->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
1931 /* Handle pending conflicts */
1932 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1935 return SVN_NO_ERROR;
1938 /* An svn_diff_tree_processor_t function.
1940 * Called after merge_file_opened() when a node receives only text and/or
1941 * property changes between LEFT_SOURCE and RIGHT_SOURCE.
1943 * left_file and right_file can be NULL when the file is not modified.
1944 * left_props and right_props are always available.
1946 static svn_error_t *
1947 merge_file_changed(const char *relpath,
1948 const svn_diff_source_t *left_source,
1949 const svn_diff_source_t *right_source,
1950 const char *left_file,
1951 const char *right_file,
1952 /*const*/ apr_hash_t *left_props,
1953 /*const*/ apr_hash_t *right_props,
1954 svn_boolean_t file_modified,
1955 const apr_array_header_t *prop_changes,
1957 const struct svn_diff_tree_processor_t *processor,
1958 apr_pool_t *scratch_pool)
1960 merge_cmd_baton_t *merge_b = processor->baton;
1961 struct merge_file_baton_t *fb = file_baton;
1962 svn_client_ctx_t *ctx = merge_b->ctx;
1963 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
1964 relpath, scratch_pool);
1965 const svn_wc_conflict_version_t *left;
1966 const svn_wc_conflict_version_t *right;
1967 svn_wc_notify_state_t text_state;
1968 svn_wc_notify_state_t property_state;
1970 SVN_ERR_ASSERT(local_abspath && svn_dirent_is_absolute(local_abspath));
1971 SVN_ERR_ASSERT(!left_file || svn_dirent_is_absolute(left_file));
1972 SVN_ERR_ASSERT(!right_file || svn_dirent_is_absolute(right_file));
1974 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1978 if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
1980 /* We haven't notified for this node yet: report a skip */
1981 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
1982 svn_wc_notify_update_shadowed_update,
1983 fb->skip_reason, fb->parent_baton,
1987 return SVN_NO_ERROR;
1990 /* This callback is essentially no more than a wrapper around
1991 svn_wc_merge5(). Thank goodness that all the
1992 diff-editor-mechanisms are doing the hard work of getting the
1995 property_state = svn_wc_notify_state_unchanged;
1996 text_state = svn_wc_notify_state_unchanged;
1998 SVN_ERR(prepare_merge_props_changed(&prop_changes, local_abspath,
1999 prop_changes, merge_b,
2000 scratch_pool, scratch_pool));
2002 SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
2003 svn_node_file, &merge_b->merge_source, merge_b->target,
2004 scratch_pool, scratch_pool));
2006 /* Do property merge now, if we are not going to perform a text merge */
2007 if ((merge_b->record_only || !left_file) && prop_changes->nelts)
2009 SVN_ERR(svn_wc_merge_props3(&property_state, ctx->wc_ctx, local_abspath,
2011 left_props, prop_changes,
2014 ctx->cancel_func, ctx->cancel_baton,
2016 if (property_state == svn_wc_notify_state_conflicted)
2018 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
2023 /* Easy out: We are only applying mergeinfo differences. */
2024 if (merge_b->record_only)
2030 svn_boolean_t has_local_mods;
2031 enum svn_wc_merge_outcome_t content_outcome;
2032 const char *target_label;
2033 const char *left_label;
2034 const char *right_label;
2035 const char *path_ext = "";
2037 if (merge_b->ext_patterns && merge_b->ext_patterns->nelts)
2039 svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
2041 && svn_cstring_match_glob_list(path_ext,
2042 merge_b->ext_patterns)))
2048 /* xgettext: the '.working', '.merge-left.r%ld' and
2049 '.merge-right.r%ld' strings are used to tag onto a file
2050 name in case of a merge conflict */
2052 target_label = apr_psprintf(scratch_pool, _(".working%s%s"),
2053 *path_ext ? "." : "", path_ext);
2054 left_label = apr_psprintf(scratch_pool,
2055 _(".merge-left.r%ld%s%s"),
2056 left_source->revision,
2057 *path_ext ? "." : "", path_ext);
2058 right_label = apr_psprintf(scratch_pool,
2059 _(".merge-right.r%ld%s%s"),
2060 right_source->revision,
2061 *path_ext ? "." : "", path_ext);
2063 SVN_ERR(svn_wc_text_modified_p2(&has_local_mods, ctx->wc_ctx,
2064 local_abspath, FALSE, scratch_pool));
2066 /* Do property merge and text merge in one step so that keyword expansion
2067 takes into account the new property values. */
2068 SVN_ERR(svn_wc_merge5(&content_outcome, &property_state, ctx->wc_ctx,
2069 left_file, right_file, local_abspath,
2070 left_label, right_label, target_label,
2072 merge_b->dry_run, merge_b->diff3_cmd,
2073 merge_b->merge_options,
2074 left_props, prop_changes,
2080 if (content_outcome == svn_wc_merge_conflict
2081 || property_state == svn_wc_notify_state_conflicted)
2083 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
2087 if (content_outcome == svn_wc_merge_conflict)
2088 text_state = svn_wc_notify_state_conflicted;
2089 else if (has_local_mods
2090 && content_outcome != svn_wc_merge_unchanged)
2091 text_state = svn_wc_notify_state_merged;
2092 else if (content_outcome == svn_wc_merge_merged)
2093 text_state = svn_wc_notify_state_changed;
2094 else if (content_outcome == svn_wc_merge_no_merge)
2095 text_state = svn_wc_notify_state_missing;
2096 else /* merge_outcome == svn_wc_merge_unchanged */
2097 text_state = svn_wc_notify_state_unchanged;
2100 if (text_state == svn_wc_notify_state_conflicted
2101 || text_state == svn_wc_notify_state_merged
2102 || text_state == svn_wc_notify_state_changed
2103 || property_state == svn_wc_notify_state_conflicted
2104 || property_state == svn_wc_notify_state_merged
2105 || property_state == svn_wc_notify_state_changed)
2107 SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
2108 text_state, property_state,
2112 return SVN_NO_ERROR;
2115 /* An svn_diff_tree_processor_t function.
2117 * Called after merge_file_opened() when a node doesn't exist in LEFT_SOURCE,
2118 * but does in RIGHT_SOURCE.
2120 * When a node is replaced instead of just added a separate opened+deleted will
2121 * be invoked before the current open+added.
2123 static svn_error_t *
2124 merge_file_added(const char *relpath,
2125 const svn_diff_source_t *copyfrom_source,
2126 const svn_diff_source_t *right_source,
2127 const char *copyfrom_file,
2128 const char *right_file,
2129 /*const*/ apr_hash_t *copyfrom_props,
2130 /*const*/ apr_hash_t *right_props,
2132 const struct svn_diff_tree_processor_t *processor,
2133 apr_pool_t *scratch_pool)
2135 merge_cmd_baton_t *merge_b = processor->baton;
2136 struct merge_file_baton_t *fb = file_baton;
2137 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2138 relpath, scratch_pool);
2139 apr_hash_t *pristine_props;
2140 apr_hash_t *new_props;
2142 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2144 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2148 if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
2150 /* We haven't notified for this node yet: report a skip */
2151 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
2152 svn_wc_notify_update_shadowed_add,
2153 fb->skip_reason, fb->parent_baton,
2157 return SVN_NO_ERROR;
2160 /* Easy out: We are only applying mergeinfo differences. */
2161 if (merge_b->record_only)
2163 return SVN_NO_ERROR;
2166 if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
2167 && ( !fb->parent_baton || !fb->parent_baton->added))
2169 /* Store the roots of added subtrees */
2170 store_path(merge_b->added_abspaths, local_abspath);
2173 if (!merge_b->dry_run)
2175 const char *copyfrom_url;
2176 svn_revnum_t copyfrom_rev;
2177 svn_stream_t *new_contents, *pristine_contents;
2179 /* If this is a merge from the same repository as our
2180 working copy, we handle adds as add-with-history.
2181 Otherwise, we'll use a pure add. */
2182 if (merge_b->same_repos)
2185 svn_dirent_skip_ancestor(merge_b->target->abspath,
2187 SVN_ERR_ASSERT(child != NULL);
2188 copyfrom_url = svn_path_url_add_component2(
2189 merge_b->merge_source.loc2->url,
2190 child, scratch_pool);
2191 copyfrom_rev = right_source->revision;
2192 SVN_ERR(check_repos_match(merge_b->target, local_abspath,
2193 copyfrom_url, scratch_pool));
2194 SVN_ERR(svn_stream_open_readonly(&pristine_contents,
2198 new_contents = NULL; /* inherit from new_base_contents */
2200 pristine_props = right_props; /* Includes last_* information */
2201 new_props = NULL; /* No local changes */
2203 if (svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
2205 alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
2206 local_abspath, merge_b->pool);
2211 apr_array_header_t *regular_props;
2213 copyfrom_url = NULL;
2214 copyfrom_rev = SVN_INVALID_REVNUM;
2216 pristine_contents = svn_stream_empty(scratch_pool);
2217 SVN_ERR(svn_stream_open_readonly(&new_contents, right_file,
2218 scratch_pool, scratch_pool));
2220 pristine_props = apr_hash_make(scratch_pool); /* Local addition */
2222 /* We don't want any foreign properties */
2223 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props,
2225 NULL, NULL, ®ular_props,
2228 new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
2230 /* Issue #3383: We don't want mergeinfo from a foreign repository. */
2231 svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
2234 /* Do everything like if we had called 'svn cp PATH1 PATH2'. */
2235 SVN_ERR(svn_wc_add_repos_file4(merge_b->ctx->wc_ctx,
2239 pristine_props, new_props,
2240 copyfrom_url, copyfrom_rev,
2241 merge_b->ctx->cancel_func,
2242 merge_b->ctx->cancel_baton,
2245 /* Caller must call svn_sleep_for_timestamps() */
2246 *merge_b->use_sleep = TRUE;
2249 SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_file,
2250 fb->add_is_replace, scratch_pool));
2252 return SVN_NO_ERROR;
2255 /* Compare the two sets of properties PROPS1 and PROPS2, ignoring the
2256 * "svn:mergeinfo" property, and noticing only "normal" props. Set *SAME to
2257 * true if the rest of the properties are identical or false if they differ.
2259 static svn_error_t *
2260 properties_same_p(svn_boolean_t *same,
2263 apr_pool_t *scratch_pool)
2265 apr_array_header_t *prop_changes;
2268 /* Examine the properties that differ */
2269 SVN_ERR(svn_prop_diffs(&prop_changes, props1, props2, scratch_pool));
2271 for (i = 0; i < prop_changes->nelts; i++)
2273 const char *pname = APR_ARRAY_IDX(prop_changes, i, svn_prop_t).name;
2275 /* Count the properties we're interested in; ignore the rest */
2276 if (svn_wc_is_normal_prop(pname)
2277 && strcmp(pname, SVN_PROP_MERGEINFO) != 0)
2280 *same = (diffs == 0);
2281 return SVN_NO_ERROR;
2284 /* Compare the file OLDER_ABSPATH (together with its normal properties in
2285 * ORIGINAL_PROPS which may also contain WC props and entry props) with the
2286 * versioned file MINE_ABSPATH (together with its versioned properties).
2287 * Set *SAME to true if they are the same or false if they differ, ignoring
2288 * the "svn:mergeinfo" property, and ignoring differences in keyword
2289 * expansion and end-of-line style. */
2290 static svn_error_t *
2291 files_same_p(svn_boolean_t *same,
2292 const char *older_abspath,
2293 apr_hash_t *original_props,
2294 const char *mine_abspath,
2295 svn_wc_context_t *wc_ctx,
2296 apr_pool_t *scratch_pool)
2298 apr_hash_t *working_props;
2300 SVN_ERR(svn_wc_prop_list2(&working_props, wc_ctx, mine_abspath,
2301 scratch_pool, scratch_pool));
2303 /* Compare the properties */
2304 SVN_ERR(properties_same_p(same, original_props, working_props,
2308 svn_stream_t *mine_stream;
2309 svn_stream_t *older_stream;
2310 svn_opt_revision_t working_rev = { svn_opt_revision_working, { 0 } };
2312 /* Compare the file content, translating 'mine' to 'normal' form. */
2313 if (svn_prop_get_value(working_props, SVN_PROP_SPECIAL) != NULL)
2314 SVN_ERR(svn_subst_read_specialfile(&mine_stream, mine_abspath,
2315 scratch_pool, scratch_pool));
2317 SVN_ERR(svn_client__get_normalized_stream(&mine_stream, wc_ctx,
2318 mine_abspath, &working_rev,
2319 FALSE, TRUE, NULL, NULL,
2320 scratch_pool, scratch_pool));
2322 SVN_ERR(svn_stream_open_readonly(&older_stream, older_abspath,
2323 scratch_pool, scratch_pool));
2325 SVN_ERR(svn_stream_contents_same2(same, mine_stream, older_stream,
2330 return SVN_NO_ERROR;
2333 /* An svn_diff_tree_processor_t function.
2335 * Called after merge_file_opened() when a node does exist in LEFT_SOURCE, but
2336 * no longer exists (or is replaced) in RIGHT_SOURCE.
2338 * When a node is replaced instead of just added a separate opened+added will
2339 * be invoked after the current open+deleted.
2341 static svn_error_t *
2342 merge_file_deleted(const char *relpath,
2343 const svn_diff_source_t *left_source,
2344 const char *left_file,
2345 /*const*/ apr_hash_t *left_props,
2347 const struct svn_diff_tree_processor_t *processor,
2348 apr_pool_t *scratch_pool)
2350 merge_cmd_baton_t *merge_b = processor->baton;
2351 struct merge_file_baton_t *fb = file_baton;
2352 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2353 relpath, scratch_pool);
2356 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2360 if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
2362 /* We haven't notified for this node yet: report a skip */
2363 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
2364 svn_wc_notify_update_shadowed_delete,
2365 fb->skip_reason, fb->parent_baton,
2369 return SVN_NO_ERROR;
2372 /* Easy out: We are only applying mergeinfo differences. */
2373 if (merge_b->record_only)
2375 return SVN_NO_ERROR;
2378 /* If the files are identical, attempt deletion */
2379 if (merge_b->force_delete)
2382 SVN_ERR(files_same_p(&same, left_file, left_props,
2383 local_abspath, merge_b->ctx->wc_ctx,
2386 if (fb->parent_baton
2387 && fb->parent_baton->delete_state)
2391 /* Note that we checked this file */
2392 store_path(fb->parent_baton->delete_state->compared_abspaths,
2397 /* We found some modification. Parent should raise a tree conflict */
2398 fb->parent_baton->delete_state->found_edit = TRUE;
2401 return SVN_NO_ERROR;
2405 if (!merge_b->dry_run)
2406 SVN_ERR(svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
2407 FALSE /* keep_local */, FALSE /* unversioned */,
2408 merge_b->ctx->cancel_func,
2409 merge_b->ctx->cancel_baton,
2410 NULL, NULL /* no notify */,
2413 /* Record that we might have deleted mergeinfo */
2414 alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
2415 local_abspath, merge_b->pool);
2417 /* And notify the deletion */
2418 SVN_ERR(record_update_delete(merge_b, fb->parent_baton, local_abspath,
2419 svn_node_file, scratch_pool));
2423 /* The files differ, so raise a conflict instead of deleting */
2425 /* This is use case 5 described in the paper attached to issue
2426 * #2282. See also notes/tree-conflicts/detection.txt
2428 SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
2430 svn_wc_conflict_action_delete,
2431 svn_wc_conflict_reason_edited,
2436 return SVN_NO_ERROR;
2439 /* An svn_diff_tree_processor_t function.
2441 Called before either merge_dir_changed(), merge_dir_added(),
2442 merge_dir_deleted() or merge_dir_closed(), unless it sets *SKIP to TRUE.
2444 After this call and before the close call, all descendants will receive
2445 their changes, unless *SKIP_CHILDREN is set to TRUE.
2447 When *SKIP is TRUE, the diff driver avoids work on getting the details
2448 for the closing callbacks.
2450 The SKIP and SKIP_DESCENDANTS work independantly.
2452 static svn_error_t *
2453 merge_dir_opened(void **new_dir_baton,
2454 svn_boolean_t *skip,
2455 svn_boolean_t *skip_children,
2456 const char *relpath,
2457 const svn_diff_source_t *left_source,
2458 const svn_diff_source_t *right_source,
2459 const svn_diff_source_t *copyfrom_source,
2460 void *parent_dir_baton,
2461 const struct svn_diff_tree_processor_t *processor,
2462 apr_pool_t *result_pool,
2463 apr_pool_t *scratch_pool)
2465 merge_cmd_baton_t *merge_b = processor->baton;
2466 struct merge_dir_baton_t *db;
2467 struct merge_dir_baton_t *pdb = parent_dir_baton;
2469 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2470 relpath, scratch_pool);
2472 db = apr_pcalloc(result_pool, sizeof(*db));
2473 db->pool = result_pool;
2474 db->tree_conflict_reason = CONFLICT_REASON_NONE;
2475 db->tree_conflict_action = svn_wc_conflict_action_edit;
2476 db->skip_reason = svn_wc_notify_state_unknown;
2478 *new_dir_baton = db;
2482 db->parent_baton = pdb;
2483 db->shadowed = pdb->shadowed;
2484 db->skip_reason = pdb->skip_reason;
2489 /* An ancestor is tree conflicted. Nothing to do here. */
2493 else if (left_source != NULL)
2495 /* Node is expected to be a directory. */
2496 svn_node_kind_t kind;
2497 svn_boolean_t is_deleted;
2498 svn_boolean_t excluded;
2499 svn_depth_t parent_depth;
2502 db->tree_conflict_action = svn_wc_conflict_action_delete;
2504 /* Check for an obstructed or missing node on disk. */
2506 svn_wc_notify_state_t obstr_state;
2507 SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
2508 &kind, &parent_depth,
2509 merge_b, local_abspath,
2512 if (obstr_state != svn_wc_notify_state_inapplicable)
2514 db->shadowed = TRUE;
2516 if (obstr_state == svn_wc_notify_state_obstructed)
2518 svn_boolean_t is_wcroot;
2520 SVN_ERR(svn_wc_check_root(&is_wcroot, NULL, NULL,
2521 merge_b->ctx->wc_ctx,
2522 local_abspath, scratch_pool));
2526 db->tree_conflict_reason = CONFLICT_REASON_SKIP_WC;
2527 return SVN_NO_ERROR;
2531 db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2532 db->skip_reason = obstr_state;
2536 *skip = *skip_children = TRUE;
2537 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath,
2541 return SVN_NO_ERROR;
2545 kind = svn_node_none;
2548 if (kind == svn_node_none)
2550 db->shadowed = TRUE;
2552 /* If this is not the merge target and the parent is too shallow to
2553 contain this directory, and the directory is not presen
2554 via exclusion or depth filtering, skip it instead of recording
2557 Non-inheritable mergeinfo will be recorded, allowing
2558 future merges into non-shallow working copies to merge
2559 changes we missed this time around. */
2560 if (pdb && (excluded
2561 || (parent_depth != svn_depth_unknown &&
2562 parent_depth < svn_depth_immediates)))
2564 db->shadowed = TRUE;
2566 db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2567 db->skip_reason = svn_wc_notify_state_missing;
2569 return SVN_NO_ERROR;
2573 db->tree_conflict_reason = svn_wc_conflict_reason_deleted;
2575 db->tree_conflict_reason = svn_wc_conflict_reason_missing;
2577 /* ### To avoid breaking tests */
2579 *skip_children = TRUE;
2580 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2581 return SVN_NO_ERROR;
2582 /* ### /avoid breaking tests */
2584 else if (kind != svn_node_dir)
2586 db->shadowed = TRUE;
2588 db->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
2590 /* ### To avoid breaking tests */
2592 *skip_children = TRUE;
2593 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2594 return SVN_NO_ERROR;
2595 /* ### /avoid breaking tests */
2600 /* We want to delete the directory */
2601 /* Mark PB edited now? */
2602 db->tree_conflict_action = svn_wc_conflict_action_delete;
2603 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2607 *skip_children = TRUE;
2608 return SVN_NO_ERROR; /* Already set a tree conflict */
2611 db->delete_state = (pdb != NULL) ? pdb->delete_state : NULL;
2613 if (db->delete_state && db->delete_state->found_edit)
2615 /* A sibling found a conflict. Done. */
2617 *skip_children = TRUE;
2619 else if (merge_b->force_delete)
2621 /* No comparison necessary */
2622 *skip_children = TRUE;
2624 else if (! db->delete_state)
2626 /* Start descendant comparison */
2627 db->delete_state = apr_pcalloc(db->pool,
2628 sizeof(*db->delete_state));
2630 db->delete_state->del_root = db;
2631 db->delete_state->compared_abspaths = apr_hash_make(db->pool);
2637 const svn_wc_conflict_description2_t *old_tc = NULL;
2639 /* The node doesn't exist pre-merge: We have an addition */
2641 db->tree_conflict_action = svn_wc_conflict_action_add;
2643 if (pdb && pdb->pending_deletes
2644 && svn_hash_gets(pdb->pending_deletes, local_abspath))
2646 db->add_is_replace = TRUE;
2647 db->tree_conflict_action = svn_wc_conflict_action_replace;
2649 svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
2653 && pdb->new_tree_conflicts
2654 && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
2656 db->tree_conflict_action = svn_wc_conflict_action_replace;
2657 db->tree_conflict_reason = old_tc->reason;
2659 if (old_tc->reason == svn_wc_conflict_reason_deleted
2660 || old_tc->reason == svn_wc_conflict_reason_moved_away)
2662 /* Issue #3806: Incoming replacements on local deletes produce
2663 inconsistent result.
2665 In this specific case we can continue applying the add part
2666 of the replacement. */
2671 *skip_children = TRUE;
2673 /* Update the tree conflict to store that this is a replace */
2674 SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
2676 db->tree_conflict_action,
2677 db->tree_conflict_reason,
2681 return SVN_NO_ERROR;
2685 if (! (merge_b->dry_run
2686 && ((pdb && pdb->added) || db->add_is_replace)))
2688 svn_wc_notify_state_t obstr_state;
2689 svn_node_kind_t kind;
2690 svn_boolean_t is_deleted;
2692 SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
2694 merge_b, local_abspath,
2697 /* In this case of adding a directory, we have an exception to the
2698 * usual "skip if it's inconsistent" rule. If the directory exists
2699 * on disk unexpectedly, we simply make it versioned, because we can
2700 * do so without risk of destroying data. Only skip if it is
2701 * versioned but unexpectedly missing from disk, or is unversioned
2702 * but obstructed by a node of the wrong kind. */
2703 if (obstr_state == svn_wc_notify_state_obstructed
2704 && (is_deleted || kind == svn_node_none))
2706 svn_node_kind_t disk_kind;
2708 SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
2711 if (disk_kind == svn_node_dir)
2713 obstr_state = svn_wc_notify_state_inapplicable;
2714 db->add_existing = TRUE; /* Take over existing directory */
2718 if (obstr_state != svn_wc_notify_state_inapplicable)
2720 /* Skip the obstruction */
2721 db->shadowed = TRUE;
2722 db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2723 db->skip_reason = obstr_state;
2725 else if (kind != svn_node_none && !is_deleted)
2727 /* Set a tree conflict */
2728 db->shadowed = TRUE;
2729 db->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
2731 if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
2732 && !(pdb && pdb->shadowed))
2734 store_path(merge_b->skipped_abspaths, local_abspath);
2739 /* Handle pending conflicts */
2740 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2744 /* Notified and done. Skip children? */
2746 else if (merge_b->record_only)
2748 /* Ok, we are done for this node and its descendants */
2750 *skip_children = TRUE;
2752 else if (! merge_b->dry_run)
2754 /* Create the directory on disk, to allow descendants to be added */
2755 if (! db->add_existing)
2756 SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT,
2761 /* svn_wc_add4 and svn_wc_add_from_disk2 can't add a node
2762 over an existing tree conflict */
2764 /* ### These functions should take some tree conflict argument
2765 and allow overwriting the tc when one is passed */
2767 SVN_ERR(svn_wc__del_tree_conflict(merge_b->ctx->wc_ctx,
2772 if (merge_b->same_repos)
2774 const char *original_url;
2776 original_url = svn_path_url_add_component2(
2777 merge_b->merge_source.loc2->url,
2778 relpath, scratch_pool);
2780 /* Limitation (aka HACK):
2781 We create a newly added directory with an original URL and
2782 revision as that in the repository, but without its properties
2785 When the merge is cancelled before the final dir_added(), the
2786 copy won't really represent the in-repository state of the node.
2788 SVN_ERR(svn_wc_add4(merge_b->ctx->wc_ctx, local_abspath,
2791 right_source->revision,
2792 merge_b->ctx->cancel_func,
2793 merge_b->ctx->cancel_baton,
2794 NULL, NULL /* no notify! */,
2799 SVN_ERR(svn_wc_add_from_disk2(merge_b->ctx->wc_ctx, local_abspath,
2800 apr_hash_make(scratch_pool),
2801 NULL, NULL /* no notify! */,
2807 /* ### Should be atomic with svn_wc_add(4|_from_disk2)() */
2808 SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
2810 db->tree_conflict_action,
2811 db->tree_conflict_reason,
2817 if (! db->shadowed && !merge_b->record_only)
2818 SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_dir,
2819 db->add_is_replace, scratch_pool));
2821 return SVN_NO_ERROR;
2824 /* An svn_diff_tree_processor_t function.
2826 * Called after merge_dir_opened() when a node exists in both the left and
2827 * right source, but has its properties changed inbetween.
2829 * After the merge_dir_opened() but before the call to this merge_dir_changed()
2830 * function all descendants will have been updated.
2832 static svn_error_t *
2833 merge_dir_changed(const char *relpath,
2834 const svn_diff_source_t *left_source,
2835 const svn_diff_source_t *right_source,
2836 /*const*/ apr_hash_t *left_props,
2837 /*const*/ apr_hash_t *right_props,
2838 const apr_array_header_t *prop_changes,
2840 const struct svn_diff_tree_processor_t *processor,
2841 apr_pool_t *scratch_pool)
2843 merge_cmd_baton_t *merge_b = processor->baton;
2844 struct merge_dir_baton_t *db = dir_baton;
2845 const apr_array_header_t *props;
2846 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2847 relpath, scratch_pool);
2849 SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
2851 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2855 if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
2857 /* We haven't notified for this node yet: report a skip */
2858 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
2859 svn_wc_notify_update_shadowed_update,
2860 db->skip_reason, db->parent_baton,
2864 return SVN_NO_ERROR;
2867 SVN_ERR(prepare_merge_props_changed(&props, local_abspath, prop_changes,
2868 merge_b, scratch_pool, scratch_pool));
2872 const svn_wc_conflict_version_t *left;
2873 const svn_wc_conflict_version_t *right;
2874 svn_client_ctx_t *ctx = merge_b->ctx;
2875 svn_wc_notify_state_t prop_state;
2877 SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
2878 svn_node_dir, &merge_b->merge_source,
2880 scratch_pool, scratch_pool));
2882 SVN_ERR(svn_wc_merge_props3(&prop_state, ctx->wc_ctx, local_abspath,
2887 ctx->cancel_func, ctx->cancel_baton,
2890 if (prop_state == svn_wc_notify_state_conflicted)
2892 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
2896 if (prop_state == svn_wc_notify_state_conflicted
2897 || prop_state == svn_wc_notify_state_merged
2898 || prop_state == svn_wc_notify_state_changed)
2900 SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
2901 svn_wc_notify_state_inapplicable,
2902 prop_state, scratch_pool));
2906 return SVN_NO_ERROR;
2910 /* An svn_diff_tree_processor_t function.
2912 * Called after merge_dir_opened() when a node doesn't exist in LEFT_SOURCE,
2913 * but does in RIGHT_SOURCE. After the merge_dir_opened() but before the call
2914 * to this merge_dir_added() function all descendants will have been added.
2916 * When a node is replaced instead of just added a separate opened+deleted will
2917 * be invoked before the current open+added.
2919 static svn_error_t *
2920 merge_dir_added(const char *relpath,
2921 const svn_diff_source_t *copyfrom_source,
2922 const svn_diff_source_t *right_source,
2923 /*const*/ apr_hash_t *copyfrom_props,
2924 /*const*/ apr_hash_t *right_props,
2926 const struct svn_diff_tree_processor_t *processor,
2927 apr_pool_t *scratch_pool)
2929 merge_cmd_baton_t *merge_b = processor->baton;
2930 struct merge_dir_baton_t *db = dir_baton;
2931 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2932 relpath, scratch_pool);
2934 /* For consistency; usually a no-op from _dir_added() */
2935 SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
2936 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2940 if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
2942 /* We haven't notified for this node yet: report a skip */
2943 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
2944 svn_wc_notify_update_shadowed_add,
2945 db->skip_reason, db->parent_baton,
2949 return SVN_NO_ERROR;
2953 db->edited /* Marked edited from merge_open_dir() */
2954 && ! merge_b->record_only /* Skip details from merge_open_dir() */
2957 if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
2958 && ( !db->parent_baton || !db->parent_baton->added))
2960 /* Store the roots of added subtrees */
2961 store_path(merge_b->added_abspaths, local_abspath);
2964 if (merge_b->same_repos)
2966 /* When the directory was added in merge_dir_added() we didn't update its
2967 pristine properties. Instead we receive the property changes later and
2968 apply them in this function.
2970 If we would apply them as changes (such as before fixing issue #3405),
2971 we would see the unmodified properties as local changes, and the
2972 pristine properties would be out of sync with what the repository
2973 expects for this directory.
2975 Instead of doing that we now simply set the properties as the pristine
2976 properties via a private libsvn_wc api.
2979 const char *copyfrom_url;
2980 svn_revnum_t copyfrom_rev;
2981 const char *parent_abspath;
2984 /* Creating a hash containing regular and entry props */
2985 apr_hash_t *new_pristine_props = right_props;
2987 parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
2988 child = svn_dirent_is_child(merge_b->target->abspath, local_abspath, NULL);
2989 SVN_ERR_ASSERT(child != NULL);
2991 copyfrom_url = svn_path_url_add_component2(merge_b->merge_source.loc2->url,
2992 child, scratch_pool);
2993 copyfrom_rev = right_source->revision;
2995 SVN_ERR(check_repos_match(merge_b->target, parent_abspath, copyfrom_url,
2998 if (!merge_b->dry_run)
3000 SVN_ERR(svn_wc__complete_directory_add(merge_b->ctx->wc_ctx,
3003 copyfrom_url, copyfrom_rev,
3007 if (svn_hash_gets(new_pristine_props, SVN_PROP_MERGEINFO))
3009 alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
3010 local_abspath, merge_b->pool);
3015 apr_array_header_t *regular_props;
3016 apr_hash_t *new_props;
3017 svn_wc_notify_state_t prop_state;
3019 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props,
3021 NULL, NULL, ®ular_props, scratch_pool));
3023 new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
3025 svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
3027 /* ### What is the easiest way to set new_props on LOCAL_ABSPATH?
3029 ### This doesn't need a merge as we just added the node
3030 ### (or installed a tree conflict and skipped this node)*/
3032 SVN_ERR(svn_wc_merge_props3(&prop_state, merge_b->ctx->wc_ctx,
3035 apr_hash_make(scratch_pool),
3036 svn_prop_hash_to_array(new_props,
3040 merge_b->ctx->cancel_func,
3041 merge_b->ctx->cancel_baton,
3043 if (prop_state == svn_wc_notify_state_conflicted)
3045 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
3050 return SVN_NO_ERROR;
3053 /* Helper for merge_dir_deleted. Implement svn_wc_status_func4_t */
3054 static svn_error_t *
3055 verify_touched_by_del_check(void *baton,
3056 const char *local_abspath,
3057 const svn_wc_status3_t *status,
3058 apr_pool_t *scratch_pool)
3060 struct dir_delete_baton_t *delb = baton;
3062 if (svn_hash_gets(delb->compared_abspaths, local_abspath))
3063 return SVN_NO_ERROR;
3065 switch (status->node_status)
3067 case svn_wc_status_deleted:
3068 case svn_wc_status_ignored:
3069 case svn_wc_status_none:
3070 return SVN_NO_ERROR;
3073 delb->found_edit = TRUE;
3074 return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
3078 /* An svn_diff_tree_processor_t function.
3080 * Called after merge_dir_opened() when a node existed only in the left source.
3082 * After the merge_dir_opened() but before the call to this merge_dir_deleted()
3083 * function all descendants that existed in left_source will have been deleted.
3085 * If this node is replaced, an _opened() followed by a matching _add() will
3086 * be invoked after this function.
3088 static svn_error_t *
3089 merge_dir_deleted(const char *relpath,
3090 const svn_diff_source_t *left_source,
3091 /*const*/ apr_hash_t *left_props,
3093 const struct svn_diff_tree_processor_t *processor,
3094 apr_pool_t *scratch_pool)
3096 merge_cmd_baton_t *merge_b = processor->baton;
3097 struct merge_dir_baton_t *db = dir_baton;
3098 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
3099 relpath, scratch_pool);
3101 apr_hash_t *working_props;
3103 SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
3104 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
3108 if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
3110 /* We haven't notified for this node yet: report a skip */
3111 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
3112 svn_wc_notify_update_shadowed_delete,
3113 db->skip_reason, db->parent_baton,
3117 return SVN_NO_ERROR;
3120 /* Easy out: We are only applying mergeinfo differences. */
3121 if (merge_b->record_only)
3123 return SVN_NO_ERROR;
3126 SVN_ERR(svn_wc_prop_list2(&working_props,
3127 merge_b->ctx->wc_ctx, local_abspath,
3128 scratch_pool, scratch_pool));
3130 if (merge_b->force_delete)
3132 /* In this legacy mode we just assume that a directory delete
3133 matches any directory. db->delete_state is NULL */
3138 struct dir_delete_baton_t *delb;
3140 /* Compare the properties */
3141 SVN_ERR(properties_same_p(&same, left_props, working_props,
3143 delb = db->delete_state;
3144 assert(delb != NULL);
3148 delb->found_edit = TRUE;
3152 store_path(delb->compared_abspaths, local_abspath);
3155 if (delb->del_root != db)
3156 return SVN_NO_ERROR;
3158 if (delb->found_edit)
3162 apr_array_header_t *ignores;
3166 SVN_ERR(svn_wc_get_default_ignores(&ignores, merge_b->ctx->config,
3169 /* None of the descendants was modified, but maybe there are
3170 descendants we haven't walked?
3172 Note that we aren't interested in changes, as we already verified
3173 changes in the paths touched by the merge. And the existence of
3174 other paths is enough to mark the directory edited */
3175 err = svn_wc_walk_status(merge_b->ctx->wc_ctx, local_abspath,
3176 svn_depth_infinity, TRUE /* get-all */,
3177 FALSE /* no-ignore */,
3178 TRUE /* ignore-text-mods */, ignores,
3179 verify_touched_by_del_check, delb,
3180 merge_b->ctx->cancel_func,
3181 merge_b->ctx->cancel_baton,
3186 if (err->apr_err != SVN_ERR_CEASE_INVOCATION)
3187 return svn_error_trace(err);
3189 svn_error_clear(err);
3192 same = ! delb->found_edit;
3196 if (same && !merge_b->dry_run)
3200 err = svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
3201 FALSE /* keep_local */, FALSE /* unversioned */,
3202 merge_b->ctx->cancel_func,
3203 merge_b->ctx->cancel_baton,
3204 NULL, NULL /* no notify */,
3209 if (err->apr_err != SVN_ERR_WC_LEFT_LOCAL_MOD)
3210 return svn_error_trace(err);
3212 svn_error_clear(err);
3219 /* If the attempt to delete an existing directory failed,
3220 * the directory has local modifications (e.g. locally added
3221 * files, or property changes). Flag a tree conflict. */
3223 /* This handles use case 5 described in the paper attached to issue
3224 * #2282. See also notes/tree-conflicts/detection.txt
3226 SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
3228 svn_wc_conflict_action_delete,
3229 svn_wc_conflict_reason_edited,
3235 /* Record that we might have deleted mergeinfo */
3237 && svn_hash_gets(working_props, SVN_PROP_MERGEINFO))
3239 alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
3240 local_abspath, merge_b->pool);
3243 SVN_ERR(record_update_delete(merge_b, db->parent_baton, local_abspath,
3244 svn_node_dir, scratch_pool));
3247 return SVN_NO_ERROR;
3250 /* An svn_diff_tree_processor_t function.
3252 * Called after merge_dir_opened() when a node itself didn't change between
3253 * the left and right source.
3255 * After the merge_dir_opened() but before the call to this merge_dir_closed()
3256 * function all descendants will have been processed.
3258 static svn_error_t *
3259 merge_dir_closed(const char *relpath,
3260 const svn_diff_source_t *left_source,
3261 const svn_diff_source_t *right_source,
3263 const struct svn_diff_tree_processor_t *processor,
3264 apr_pool_t *scratch_pool)
3266 merge_cmd_baton_t *merge_b = processor->baton;
3267 struct merge_dir_baton_t *db = dir_baton;
3269 SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
3271 return SVN_NO_ERROR;
3274 /* An svn_diff_tree_processor_t function.
3276 Called when the diff driver wants to report an absent path.
3278 In case of merges this happens when the diff encounters a server-excluded
3281 We register a skipped path, which will make parent mergeinfo non-
3282 inheritable. This ensures that a future merge might see these skipped
3283 changes as eligable for merging.
3285 For legacy reasons we also notify the path as skipped.
3287 static svn_error_t *
3288 merge_node_absent(const char *relpath,
3290 const svn_diff_tree_processor_t *processor,
3291 apr_pool_t *scratch_pool)
3293 merge_cmd_baton_t *merge_b = processor->baton;
3294 struct merge_dir_baton_t *db = dir_baton;
3296 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
3297 relpath, scratch_pool);
3299 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_unknown,
3300 svn_wc_notify_skip, svn_wc_notify_state_missing,
3303 return SVN_NO_ERROR;
3306 /*-----------------------------------------------------------------------*/
3308 /*** Merge Notification ***/
3311 /* Finds a nearest ancestor in CHILDREN_WITH_MERGEINFO for LOCAL_ABSPATH. If
3312 PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
3313 where child->abspath == PATH is considered PATH's ancestor. If FALSE,
3314 then child->abspath must be a proper ancestor of PATH.
3316 CHILDREN_WITH_MERGEINFO is expected to be sorted in Depth first
3318 static svn_client__merge_path_t *
3319 find_nearest_ancestor(const apr_array_header_t *children_with_mergeinfo,
3320 svn_boolean_t path_is_own_ancestor,
3321 const char *local_abspath)
3325 SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
3327 for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
3329 svn_client__merge_path_t *child =
3330 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
3332 if (svn_dirent_is_ancestor(child->abspath, local_abspath)
3333 && (path_is_own_ancestor
3334 || strcmp(child->abspath, local_abspath) != 0))
3340 /* Find the highest level path in a merge target (possibly the merge target
3341 itself) to use in a merge notification header.
3343 Return the svn_client__merge_path_t * representing the most distant
3344 ancestor in CHILDREN_WITH_MERGEINFO of LOCAL_ABSPATH where said
3345 ancestor's first remaining ranges element (per the REMAINING_RANGES
3346 member of the ancestor) intersect with the first remaining ranges element
3347 for every intermediate ancestor svn_client__merge_path_t * of
3348 LOCAL_ABSPATH. If no such ancestor is found return NULL.
3350 If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
3351 represent a forward merge, then set *START to the oldest revision found
3352 in any of the intersecting ancestors and *END to the youngest revision
3353 found. If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
3354 represent a reverse merge, then set *START to the youngest revision
3355 found and *END to the oldest revision found. If no ancestors are found
3356 then set *START and *END to SVN_INVALID_REVNUM.
3358 If PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
3359 where child->abspath == PATH is considered PATH's ancestor. If FALSE,
3360 then child->abspath must be a proper ancestor of PATH.
3362 See the CHILDREN_WITH_MERGEINFO ARRAY global comment for more
3364 static svn_client__merge_path_t *
3365 find_nearest_ancestor_with_intersecting_ranges(
3366 svn_revnum_t *start,
3368 const apr_array_header_t *children_with_mergeinfo,
3369 svn_boolean_t path_is_own_ancestor,
3370 const char *local_abspath)
3373 svn_client__merge_path_t *nearest_ancestor = NULL;
3375 *start = SVN_INVALID_REVNUM;
3376 *end = SVN_INVALID_REVNUM;
3378 SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
3380 for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
3382 svn_client__merge_path_t *child =
3383 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
3385 if (svn_dirent_is_ancestor(child->abspath, local_abspath)
3386 && (path_is_own_ancestor
3387 || strcmp(child->abspath, local_abspath) != 0))
3389 if (nearest_ancestor == NULL)
3391 /* Found an ancestor. */
3392 nearest_ancestor = child;
3394 if (child->remaining_ranges)
3396 svn_merge_range_t *r1 = APR_ARRAY_IDX(
3397 child->remaining_ranges, 0, svn_merge_range_t *);
3403 /* If CHILD->REMAINING_RANGES is null then LOCAL_ABSPATH
3404 is inside an absent subtree in the merge target. */
3405 *start = SVN_INVALID_REVNUM;
3406 *end = SVN_INVALID_REVNUM;
3412 /* We'e found another ancestor for LOCAL_ABSPATH. Do its
3413 first remaining range intersect with the previously
3415 svn_merge_range_t *r1 =
3416 APR_ARRAY_IDX(nearest_ancestor->remaining_ranges, 0,
3417 svn_merge_range_t *);
3418 svn_merge_range_t *r2 =
3419 APR_ARRAY_IDX(child->remaining_ranges, 0,
3420 svn_merge_range_t *);
3424 svn_merge_range_t range1;
3425 svn_merge_range_t range2;
3426 svn_boolean_t reverse_merge = r1->start > r2->end;
3428 /* Flip endpoints if this is a reverse merge. */
3431 range1.start = r1->end;
3432 range1.end = r1->start;
3433 range2.start = r2->end;
3434 range2.end = r2->start;
3438 range1.start = r1->start;
3439 range1.end = r1->end;
3440 range2.start = r2->start;
3441 range2.end = r2->end;
3444 if (range1.start < range2.end && range2.start < range1.end)
3446 *start = reverse_merge ?
3447 MAX(r1->start, r2->start) : MIN(r1->start, r2->start);
3448 *end = reverse_merge ?
3449 MIN(r1->end, r2->end) : MAX(r1->end, r2->end);
3450 nearest_ancestor = child;
3456 return nearest_ancestor;
3459 /* Notify that we're starting to record mergeinfo for the merge of the
3460 * revision range RANGE into TARGET_ABSPATH. RANGE should be null if the
3461 * merge sources are not from the same URL.
3463 * This calls the client's notification receiver (as found in the client
3464 * context), with a WC abspath.
3467 notify_mergeinfo_recording(const char *target_abspath,
3468 const svn_merge_range_t *range,
3469 svn_client_ctx_t *ctx,
3472 if (ctx->notify_func2)
3474 svn_wc_notify_t *n = svn_wc_create_notify(
3475 target_abspath, svn_wc_notify_merge_record_info_begin, pool);
3477 n->merge_range = range ? svn_merge_range_dup(range, pool) : NULL;
3478 ctx->notify_func2(ctx->notify_baton2, n, pool);
3482 /* Notify that we're completing the merge into TARGET_ABSPATH.
3484 * This calls the client's notification receiver (as found in the client
3485 * context), with a WC abspath.
3488 notify_merge_completed(const char *target_abspath,
3489 svn_client_ctx_t *ctx,
3492 if (ctx->notify_func2)
3495 = svn_wc_create_notify(target_abspath, svn_wc_notify_merge_completed,
3497 ctx->notify_func2(ctx->notify_baton2, n, pool);
3501 /* Is the notification the result of a real operative merge? */
3502 #define IS_OPERATIVE_NOTIFICATION(notify) \
3503 (notify->content_state == svn_wc_notify_state_conflicted \
3504 || notify->content_state == svn_wc_notify_state_merged \
3505 || notify->content_state == svn_wc_notify_state_changed \
3506 || notify->prop_state == svn_wc_notify_state_conflicted \
3507 || notify->prop_state == svn_wc_notify_state_merged \
3508 || notify->prop_state == svn_wc_notify_state_changed \
3509 || notify->action == svn_wc_notify_update_add \
3510 || notify->action == svn_wc_notify_tree_conflict)
3513 /* Remove merge source gaps from range used for merge notifications.
3514 See http://subversion.tigris.org/issues/show_bug.cgi?id=4138
3516 If IMPLICIT_SRC_GAP is not NULL then it is a rangelist containing a
3517 single range (see the implicit_src_gap member of merge_cmd_baton_t).
3518 RANGE describes a (possibly reverse) merge.
3520 If IMPLICIT_SRC_GAP is not NULL and it's sole range intersects with
3521 the older revision in *RANGE, then remove IMPLICIT_SRC_GAP's range
3524 remove_source_gap(svn_merge_range_t *range,
3525 apr_array_header_t *implicit_src_gap)
3527 if (implicit_src_gap)
3529 svn_merge_range_t *gap_range =
3530 APR_ARRAY_IDX(implicit_src_gap, 0, svn_merge_range_t *);
3531 if (range->start < range->end)
3533 if (gap_range->start == range->start)
3534 range->start = gap_range->end;
3536 else /* Reverse merge */
3538 if (gap_range->start == range->end)
3539 range->end = gap_range->end;
3544 /* Notify that we're starting a merge
3546 * This calls the client's notification receiver (as found in the client
3547 * context), with a WC abspath.
3549 static svn_error_t *
3550 notify_merge_begin(merge_cmd_baton_t *merge_b,
3551 const char *local_abspath,
3552 svn_boolean_t delete_action,
3553 apr_pool_t *scratch_pool)
3555 svn_wc_notify_t *notify;
3556 svn_merge_range_t n_range =
3557 {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE};
3558 const char *notify_abspath;
3560 if (! merge_b->ctx->notify_func2)
3561 return SVN_NO_ERROR;
3563 /* If our merge sources are ancestors of one another... */
3564 if (merge_b->merge_source.ancestral)
3566 const svn_client__merge_path_t *child;
3567 /* Find NOTIFY->PATH's nearest ancestor in
3568 NOTIFY->CHILDREN_WITH_MERGEINFO. Normally we consider a child in
3569 NOTIFY->CHILDREN_WITH_MERGEINFO representing PATH to be an
3570 ancestor of PATH, but if this is a deletion of PATH then the
3571 notification must be for a proper ancestor of PATH. This ensures
3572 we don't get notifications like:
3574 --- Merging rX into 'PARENT/CHILD'
3579 --- Merging rX into 'PARENT'
3583 child = find_nearest_ancestor_with_intersecting_ranges(
3584 &(n_range.start), &(n_range.end),
3585 merge_b->notify_begin.nodes_with_mergeinfo,
3586 ! delete_action, local_abspath);
3588 if (!child && delete_action)
3590 /* Triggered by file replace in single-file-merge */
3591 child = find_nearest_ancestor(merge_b->notify_begin.nodes_with_mergeinfo,
3592 TRUE, local_abspath);
3595 assert(child != NULL); /* Should always find the merge anchor */
3598 return SVN_NO_ERROR;
3600 if (merge_b->notify_begin.last_abspath != NULL
3601 && strcmp(child->abspath, merge_b->notify_begin.last_abspath) == 0)
3603 /* Don't notify the same merge again */
3604 return SVN_NO_ERROR;
3607 merge_b->notify_begin.last_abspath = child->abspath;
3609 if (child->absent || child->remaining_ranges->nelts == 0
3610 || !SVN_IS_VALID_REVNUM(n_range.start))
3612 /* No valid information for an header */
3613 return SVN_NO_ERROR;
3616 notify_abspath = child->abspath;
3620 if (merge_b->notify_begin.last_abspath)
3621 return SVN_NO_ERROR; /* already notified */
3623 notify_abspath = merge_b->target->abspath;
3624 /* Store something in last_abspath. Any value would do */
3625 merge_b->notify_begin.last_abspath = merge_b->target->abspath;
3628 notify = svn_wc_create_notify(notify_abspath,
3630 ? svn_wc_notify_merge_begin
3631 : svn_wc_notify_foreign_merge_begin,
3634 if (SVN_IS_VALID_REVNUM(n_range.start))
3636 /* If the merge source has a gap, then don't mention
3637 those gap revisions in the notification. */
3638 remove_source_gap(&n_range, merge_b->implicit_src_gap);
3639 notify->merge_range = &n_range;
3643 notify->merge_range = NULL;
3646 (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
3649 return SVN_NO_ERROR;
3652 /* Set *OUT_RANGELIST to the intersection of IN_RANGELIST with the simple
3653 * (inheritable) revision range REV1:REV2, according to CONSIDER_INHERITANCE.
3654 * If REV1 is equal to REV2, the result is an empty rangelist, otherwise
3655 * REV1 must be less than REV2.
3657 * Note: If CONSIDER_INHERITANCE is FALSE, the effect is to treat any non-
3658 * inheritable input ranges as if they were inheritable. If it is TRUE, the
3659 * effect is to discard any non-inheritable input ranges. Therefore the
3660 * ranges in *OUT_RANGELIST will always be inheritable. */
3661 static svn_error_t *
3662 rangelist_intersect_range(svn_rangelist_t **out_rangelist,
3663 const svn_rangelist_t *in_rangelist,
3666 svn_boolean_t consider_inheritance,
3667 apr_pool_t *result_pool,
3668 apr_pool_t *scratch_pool)
3670 SVN_ERR_ASSERT(rev1 <= rev2);
3674 svn_rangelist_t *simple_rangelist =
3675 svn_rangelist__initialize(rev1, rev2, TRUE, scratch_pool);
3677 SVN_ERR(svn_rangelist_intersect(out_rangelist,
3678 simple_rangelist, in_rangelist,
3679 consider_inheritance, result_pool));
3683 *out_rangelist = apr_array_make(result_pool, 0,
3684 sizeof(svn_merge_range_t *));
3686 return SVN_NO_ERROR;
3689 /* Helper for fix_deleted_subtree_ranges(). Like fix_deleted_subtree_ranges()
3690 this function should only be called when honoring mergeinfo.
3692 CHILD, PARENT, REVISION1, REVISION2, and RA_SESSION are all cascaded from
3693 fix_deleted_subtree_ranges() -- see that function for more information on
3696 If PARENT is not the merge target then PARENT must have already have been
3697 processed by this function as a child. Specifically, this means that
3698 PARENT->REMAINING_RANGES must already be populated -- it can be an empty
3699 rangelist but cannot be NULL.
3701 PRIMARY_URL is the merge source url of CHILD at the younger of REVISION1
3704 Since this function is only invoked for subtrees of the merge target, the
3705 guarantees afforded by normalize_merge_sources() don't apply - see the
3706 'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file.
3707 Therefore it is possible that PRIMARY_URL@REVISION1 and
3708 PRIMARY_URL@REVISION2 don't describe the endpoints of an unbroken line of
3709 history. The purpose of this helper is to identify these cases of broken
3710 history and adjust CHILD->REMAINING_RANGES in such a way we don't later try
3711 to describe nonexistent path/revisions to the merge report editor -- see
3712 drive_merge_report_editor().
3714 If PRIMARY_URL@REVISION1 and PRIMARY_URL@REVISION2 describe an unbroken
3715 line of history then do nothing and leave CHILD->REMAINING_RANGES as-is.
3717 If neither PRIMARY_URL@REVISION1 nor PRIMARY_URL@REVISION2 exist then
3718 there is nothing to merge to CHILD->ABSPATH so set CHILD->REMAINING_RANGES
3719 equal to PARENT->REMAINING_RANGES. This will cause the subtree to
3720 effectively ignore CHILD -- see 'Note: If the first svn_merge_range_t...'
3721 in drive_merge_report_editor()'s doc string.
3723 If PRIMARY_URL@REVISION1 *xor* PRIMARY_URL@REVISION2 exist then we take the
3724 subset of REVISION1:REVISION2 in CHILD->REMAINING_RANGES at which
3725 PRIMARY_URL doesn't exist and set that subset equal to
3726 PARENT->REMAINING_RANGES' intersection with that non-existent range. Why?
3727 Because this causes CHILD->REMAINING_RANGES to be identical to
3728 PARENT->REMAINING_RANGES for revisions between REVISION1 and REVISION2 at
3729 which PRIMARY_URL doesn't exist. As mentioned above this means that
3730 drive_merge_report_editor() won't attempt to describe these non-existent
3731 subtree path/ranges to the reporter (which would break the merge).
3733 If the preceding paragraph wasn't terribly clear then what follows spells
3734 out this function's behavior a bit more explicitly:
3736 For forward merges (REVISION1 < REVISION2)
3738 If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then
3739 find the revision 'N' in which PRIMARY_URL@REVISION1 was deleted. Leave
3740 the subset of CHILD->REMAINING_RANGES that intersects with
3741 REVISION1:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3742 that intersects with (N - 1):REVISION2 equal to PARENT->REMAINING_RANGES'
3743 intersection with (N - 1):REVISION2.
3745 If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does,
3746 then find the revision 'M' in which PRIMARY_URL@REVISION2 came into
3747 existence. Leave the subset of CHILD->REMAINING_RANGES that intersects with
3748 (M - 1):REVISION2 as-is and set the subset of CHILD->REMAINING_RANGES
3749 that intersects with REVISION1:(M - 1) equal to PARENT->REMAINING_RANGES'
3750 intersection with REVISION1:(M - 1).
3752 For reverse merges (REVISION1 > REVISION2)
3754 If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then
3755 find the revision 'N' in which PRIMARY_URL@REVISION1 came into existence.
3756 Leave the subset of CHILD->REMAINING_RANGES that intersects with
3757 REVISION2:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3758 that intersects with (N - 1):REVISION1 equal to PARENT->REMAINING_RANGES'
3759 intersection with (N - 1):REVISION1.
3761 If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does,
3762 then find the revision 'M' in which PRIMARY_URL@REVISION2 came into
3763 existence. Leave the subset of CHILD->REMAINING_RANGES that intersects with
3764 REVISION2:(M - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3765 that intersects with (M - 1):REVISION1 equal to PARENT->REMAINING_RANGES'
3766 intersection with REVISION1:(M - 1).
3768 SCRATCH_POOL is used for all temporary allocations. Changes to CHILD are
3769 allocated in RESULT_POOL. */
3770 static svn_error_t *
3771 adjust_deleted_subtree_ranges(svn_client__merge_path_t *child,
3772 svn_client__merge_path_t *parent,
3773 svn_revnum_t revision1,
3774 svn_revnum_t revision2,
3775 const char *primary_url,
3776 svn_ra_session_t *ra_session,
3777 svn_client_ctx_t *ctx,
3778 apr_pool_t *result_pool,
3779 apr_pool_t *scratch_pool)
3781 svn_boolean_t is_rollback = revision2 < revision1;
3782 svn_revnum_t younger_rev = is_rollback ? revision1 : revision2;
3783 svn_revnum_t peg_rev = younger_rev;
3784 svn_revnum_t older_rev = is_rollback ? revision2 : revision1;
3785 apr_array_header_t *segments;
3788 SVN_ERR_ASSERT(parent->remaining_ranges);
3790 err = svn_client__repos_location_segments(&segments, ra_session,
3791 primary_url, peg_rev,
3792 younger_rev, older_rev, ctx,
3795 /* If PRIMARY_URL@peg_rev doesn't exist then
3796 svn_client__repos_location_segments() typically returns an
3797 SVN_ERR_FS_NOT_FOUND error, but if it doesn't exist for a
3798 forward merge over ra_neon then we get SVN_ERR_RA_DAV_REQUEST_FAILED.
3799 http://subversion.tigris.org/issues/show_bug.cgi?id=3137 fixed some of
3800 the cases where different RA layers returned different error codes to
3801 signal the "path not found"...but it looks like there is more to do.
3803 ### Do we still need to special case for ra_neon (since it no longer
3807 if (err->apr_err == SVN_ERR_FS_NOT_FOUND
3808 || err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
3810 /* PRIMARY_URL@peg_rev doesn't exist. Check if PRIMARY_URL@older_rev
3811 exists, if neither exist then the editor can simply ignore this
3813 const char *rel_source_path; /* PRIMARY_URL relative to RA_SESSION */
3814 svn_node_kind_t kind;
3816 svn_error_clear(err);
3819 SVN_ERR(svn_ra_get_path_relative_to_session(
3820 ra_session, &rel_source_path, primary_url, scratch_pool));
3822 SVN_ERR(svn_ra_check_path(ra_session, rel_source_path,
3823 older_rev, &kind, scratch_pool));
3824 if (kind == svn_node_none)
3826 /* Neither PRIMARY_URL@peg_rev nor PRIMARY_URL@older_rev exist,
3827 so there is nothing to merge. Set CHILD->REMAINING_RANGES
3828 identical to PARENT's. */
3829 child->remaining_ranges =
3830 svn_rangelist_dup(parent->remaining_ranges, scratch_pool);
3834 svn_rangelist_t *deleted_rangelist;
3835 svn_revnum_t rev_primary_url_deleted;
3837 /* PRIMARY_URL@older_rev exists, so it was deleted at some
3838 revision prior to peg_rev, find that revision. */
3839 SVN_ERR(svn_ra_get_deleted_rev(ra_session, rel_source_path,
3840 older_rev, younger_rev,
3841 &rev_primary_url_deleted,
3844 /* PRIMARY_URL@older_rev exists and PRIMARY_URL@peg_rev doesn't,
3845 so svn_ra_get_deleted_rev() should always find the revision
3846 PRIMARY_URL@older_rev was deleted. */
3847 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev_primary_url_deleted));
3849 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
3850 PARENT->REMAINING_RANGES so both will work with the
3851 svn_rangelist_* APIs below. */
3854 /* svn_rangelist_reverse operates in place so it's safe
3855 to use our scratch_pool. */
3856 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
3858 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
3862 /* Find the intersection of CHILD->REMAINING_RANGES with the
3863 range over which PRIMARY_URL@older_rev exists (ending at
3864 the youngest revision at which it still exists). */
3865 SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
3866 child->remaining_ranges,
3868 rev_primary_url_deleted - 1,
3870 scratch_pool, scratch_pool));
3872 /* Merge into CHILD->REMAINING_RANGES the intersection of
3873 PARENT->REMAINING_RANGES with the range beginning when
3874 PRIMARY_URL@older_rev was deleted until younger_rev. */
3875 SVN_ERR(rangelist_intersect_range(&deleted_rangelist,
3876 parent->remaining_ranges,
3877 rev_primary_url_deleted - 1,
3880 scratch_pool, scratch_pool));
3881 SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
3882 deleted_rangelist, scratch_pool,
3885 /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
3886 to reverse order if necessary. */
3889 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
3891 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
3898 return svn_error_trace(err);
3901 else /* PRIMARY_URL@peg_rev exists. */
3903 svn_rangelist_t *non_existent_rangelist;
3904 svn_location_segment_t *segment =
3905 APR_ARRAY_IDX(segments, (segments->nelts - 1),
3906 svn_location_segment_t *);
3908 /* We know PRIMARY_URL@peg_rev exists as the call to
3909 svn_client__repos_location_segments() succeeded. If there is only
3910 one segment that starts at oldest_rev then we know that
3911 PRIMARY_URL@oldest_rev:PRIMARY_URL@peg_rev describes an unbroken
3912 line of history, so there is nothing more to adjust in
3913 CHILD->REMAINING_RANGES. */
3914 if (segment->range_start == older_rev)
3916 return SVN_NO_ERROR;
3919 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
3920 PARENT->REMAINING_RANGES so both will work with the
3921 svn_rangelist_* APIs below. */
3924 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
3926 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
3930 /* Intersect CHILD->REMAINING_RANGES with the range where PRIMARY_URL
3931 exists. Since segment doesn't span older_rev:peg_rev we know
3932 PRIMARY_URL@peg_rev didn't come into existence until
3933 segment->range_start + 1. */
3934 SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
3935 child->remaining_ranges,
3936 segment->range_start, peg_rev,
3937 FALSE, scratch_pool, scratch_pool));
3939 /* Merge into CHILD->REMAINING_RANGES the intersection of
3940 PARENT->REMAINING_RANGES with the range before PRIMARY_URL@peg_rev
3941 came into existence. */
3942 SVN_ERR(rangelist_intersect_range(&non_existent_rangelist,
3943 parent->remaining_ranges,
3944 older_rev, segment->range_start,
3945 FALSE, scratch_pool, scratch_pool));
3946 SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
3947 non_existent_rangelist, scratch_pool,
3950 /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
3951 to reverse order if necessary. */
3954 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
3956 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
3961 /* Make a lasting copy of CHILD->REMAINING_RANGES using POOL. */
3962 child->remaining_ranges = svn_rangelist_dup(child->remaining_ranges,
3964 return SVN_NO_ERROR;
3967 /* Helper for do_directory_merge().
3969 SOURCE is cascaded from the argument of the same name in
3970 do_directory_merge(). TARGET is the merge target. RA_SESSION is the
3971 session for the younger of SOURCE->loc1 and SOURCE->loc2.
3973 Adjust the subtrees in CHILDREN_WITH_MERGEINFO so that we don't
3974 later try to describe invalid paths in drive_merge_report_editor().
3975 This function is just a thin wrapper around
3976 adjust_deleted_subtree_ranges(), which see for further details.
3978 SCRATCH_POOL is used for all temporary allocations. Changes to
3979 CHILDREN_WITH_MERGEINFO are allocated in RESULT_POOL.
3981 static svn_error_t *
3982 fix_deleted_subtree_ranges(const merge_source_t *source,
3983 const merge_target_t *target,
3984 svn_ra_session_t *ra_session,
3985 apr_array_header_t *children_with_mergeinfo,
3986 svn_client_ctx_t *ctx,
3987 apr_pool_t *result_pool,
3988 apr_pool_t *scratch_pool)
3991 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3992 svn_boolean_t is_rollback = source->loc2->rev < source->loc1->rev;
3994 assert(session_url_is(ra_session,
3995 (is_rollback ? source->loc1 : source->loc2)->url,
3998 /* CHILDREN_WITH_MERGEINFO is sorted in depth-first order, so
3999 start at index 1 to examine only subtrees. */
4000 for (i = 1; i < children_with_mergeinfo->nelts; i++)
4002 svn_client__merge_path_t *child =
4003 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
4004 svn_client__merge_path_t *parent;
4005 svn_rangelist_t *deleted_rangelist, *added_rangelist;
4007 SVN_ERR_ASSERT(child);
4011 svn_pool_clear(iterpool);
4013 /* Find CHILD's parent. */
4014 parent = find_nearest_ancestor(children_with_mergeinfo,
4015 FALSE, child->abspath);
4017 /* Since CHILD is a subtree then its parent must be in
4018 CHILDREN_WITH_MERGEINFO, see the global comment
4019 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
4020 SVN_ERR_ASSERT(parent);
4022 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
4023 so it will work with the svn_rangelist_diff API. */
4026 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
4027 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
4030 SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
4031 child->remaining_ranges,
4032 parent->remaining_ranges,
4037 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
4038 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
4041 /* If CHILD is the merge target we then know that SOURCE is provided
4042 by normalize_merge_sources() -- see 'MERGEINFO MERGE SOURCE
4043 NORMALIZATION'. Due to this normalization we know that SOURCE
4044 describes an unbroken line of history such that the entire range
4045 described by SOURCE can potentially be merged to CHILD.
4047 But if CHILD is a subtree we don't have the same guarantees about
4048 SOURCE as we do for the merge target. SOURCE->loc1 and/or
4049 SOURCE->loc2 might not exist.
4051 If one or both doesn't exist, then adjust CHILD->REMAINING_RANGES
4052 such that we don't later try to describe invalid subtrees in
4053 drive_merge_report_editor(), as that will break the merge.
4054 If CHILD has the same remaining ranges as PARENT however, then
4055 there is no need to make these adjustments, since
4056 drive_merge_report_editor() won't attempt to describe CHILD in this
4057 case, see the 'Note' in drive_merge_report_editor's docstring. */
4058 if (deleted_rangelist->nelts || added_rangelist->nelts)
4060 const char *child_primary_source_url;
4061 const char *child_repos_src_path =
4062 svn_dirent_is_child(target->abspath, child->abspath, iterpool);
4064 /* This loop is only processing subtrees, so CHILD->ABSPATH
4065 better be a proper child of the merge target. */
4066 SVN_ERR_ASSERT(child_repos_src_path);
4068 child_primary_source_url =
4069 svn_path_url_add_component2((source->loc1->rev < source->loc2->rev)
4070 ? source->loc2->url : source->loc1->url,
4071 child_repos_src_path, iterpool);
4073 SVN_ERR(adjust_deleted_subtree_ranges(child, parent,
4076 child_primary_source_url,
4078 ctx, result_pool, iterpool));
4082 svn_pool_destroy(iterpool);
4083 return SVN_NO_ERROR;
4086 /*-----------------------------------------------------------------------*/
4088 /*** Determining What Remains To Be Merged ***/
4090 /* Get explicit and/or implicit mergeinfo for the working copy path
4093 If RECORDED_MERGEINFO is not NULL then set *RECORDED_MERGEINFO
4094 to TARGET_ABSPATH's explicit or inherited mergeinfo as dictated by
4097 If IMPLICIT_MERGEINFO is not NULL then set *IMPLICIT_MERGEINFO
4098 to TARGET_ABSPATH's implicit mergeinfo (a.k.a. natural history).
4100 If both RECORDED_MERGEINFO and IMPLICIT_MERGEINFO are not NULL and
4101 *RECORDED_MERGEINFO is inherited, then *IMPLICIT_MERGEINFO will be
4102 removed from *RECORDED_MERGEINFO.
4104 If INHERITED is not NULL set *INHERITED to TRUE if *RECORDED_MERGEINFO
4105 is inherited rather than explicit. If RECORDED_MERGEINFO is NULL then
4106 INHERITED is ignored.
4109 If IMPLICIT_MERGEINFO is not NULL then START and END are limits on
4110 the natural history sought, must both be valid revision numbers, and
4111 START must be greater than END. If TARGET_ABSPATH's base revision
4112 is older than START, then the base revision is used as the younger
4113 bound in place of START.
4115 RA_SESSION is an RA session open to the repository in which TARGET_ABSPATH
4116 lives. It may be temporarily reparented as needed by this function.
4118 Allocate *RECORDED_MERGEINFO and *IMPLICIT_MERGEINFO in RESULT_POOL.
4119 Use SCRATCH_POOL for any temporary allocations. */
4120 static svn_error_t *
4121 get_full_mergeinfo(svn_mergeinfo_t *recorded_mergeinfo,
4122 svn_mergeinfo_t *implicit_mergeinfo,
4123 svn_boolean_t *inherited,
4124 svn_mergeinfo_inheritance_t inherit,
4125 svn_ra_session_t *ra_session,
4126 const char *target_abspath,
4129 svn_client_ctx_t *ctx,
4130 apr_pool_t *result_pool,
4131 apr_pool_t *scratch_pool)
4133 /* First, we get the real mergeinfo. */
4134 if (recorded_mergeinfo)
4136 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(recorded_mergeinfo,
4138 NULL /* from_repos */,
4140 inherit, ra_session,
4145 if (implicit_mergeinfo)
4147 svn_client__pathrev_t *target;
4149 /* Assert that we have sane input. */
4150 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start) && SVN_IS_VALID_REVNUM(end)
4153 /* Retrieve the origin (original_*) of the node, or just the
4154 url if the node was not copied. */
4155 SVN_ERR(svn_client__wc_node_get_origin(&target, target_abspath, ctx,
4156 scratch_pool, scratch_pool));
4160 /* We've been asked to operate on a locally added target, so its
4161 * implicit mergeinfo is empty. */
4162 *implicit_mergeinfo = apr_hash_make(result_pool);
4164 else if (target->rev <= end)
4166 /* We're asking about a range outside our natural history
4167 altogether. That means our implicit mergeinfo is empty. */
4168 *implicit_mergeinfo = apr_hash_make(result_pool);
4172 /* Fetch so-called "implicit mergeinfo" (that is, natural
4175 /* Do not ask for implicit mergeinfo from TARGET_ABSPATH's future.
4176 TARGET_ABSPATH might not even exist, and even if it does the
4177 working copy is *at* TARGET_REV so its implicit history ends
4179 if (target->rev < start)
4180 start = target->rev;
4182 /* Fetch the implicit mergeinfo. */
4183 SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo,
4189 } /*if (implicit_mergeinfo) */
4191 return SVN_NO_ERROR;
4194 /* Helper for ensure_implicit_mergeinfo().
4196 PARENT, CHILD, REVISION1, REVISION2 and CTX
4197 are all cascaded from the arguments of the same names in
4198 ensure_implicit_mergeinfo(). PARENT and CHILD must both exist, i.e.
4199 this function should never be called where CHILD is the merge target.
4201 If PARENT->IMPLICIT_MERGEINFO is NULL, obtain it from the server.
4203 Set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
4204 PARENT->IMPLICIT_MERGEINFO. CHILD->IMPLICIT_MERGEINFO is allocated
4207 RA_SESSION is an RA session open to the repository that contains CHILD.
4208 It may be temporarily reparented by this function.
4210 static svn_error_t *
4211 inherit_implicit_mergeinfo_from_parent(svn_client__merge_path_t *parent,
4212 svn_client__merge_path_t *child,
4213 svn_revnum_t revision1,
4214 svn_revnum_t revision2,
4215 svn_ra_session_t *ra_session,
4216 svn_client_ctx_t *ctx,
4217 apr_pool_t *result_pool,
4218 apr_pool_t *scratch_pool)
4220 const char *path_diff;
4222 /* This only works on subtrees! */
4223 SVN_ERR_ASSERT(parent);
4224 SVN_ERR_ASSERT(child);
4226 /* While PARENT must exist, it is possible we've deferred
4227 getting its implicit mergeinfo. If so get it now. */
4228 if (!parent->implicit_mergeinfo)
4229 SVN_ERR(get_full_mergeinfo(NULL, &(parent->implicit_mergeinfo),
4230 NULL, svn_mergeinfo_inherited,
4231 ra_session, child->abspath,
4232 MAX(revision1, revision2),
4233 MIN(revision1, revision2),
4234 ctx, result_pool, scratch_pool));
4236 /* Let CHILD inherit PARENT's implicit mergeinfo. */
4238 path_diff = svn_dirent_is_child(parent->abspath, child->abspath,
4240 /* PARENT->PATH better be an ancestor of CHILD->ABSPATH! */
4241 SVN_ERR_ASSERT(path_diff);
4243 SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
4244 &child->implicit_mergeinfo, parent->implicit_mergeinfo,
4245 path_diff, result_pool, scratch_pool));
4246 child->implicit_mergeinfo = svn_mergeinfo_dup(child->implicit_mergeinfo,
4248 return SVN_NO_ERROR;
4251 /* Helper of filter_merged_revisions().
4253 If we have deferred obtaining CHILD->IMPLICIT_MERGEINFO, then get
4254 it now, allocating it in RESULT_POOL. If CHILD_INHERITS_PARENT is true
4255 then set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
4256 PARENT->IMPLICIT_MERGEINFO, otherwise contact the repository. Use
4257 SCRATCH_POOL for all temporary allocations.
4259 RA_SESSION is an RA session open to the repository that contains CHILD.
4260 It may be temporarily reparented by this function.
4262 PARENT, CHILD, REVISION1, REVISION2 and
4263 CTX are all cascaded from the arguments of the same name in
4264 filter_merged_revisions() and the same conditions for that function
4266 static svn_error_t *
4267 ensure_implicit_mergeinfo(svn_client__merge_path_t *parent,
4268 svn_client__merge_path_t *child,
4269 svn_boolean_t child_inherits_parent,
4270 svn_revnum_t revision1,
4271 svn_revnum_t revision2,
4272 svn_ra_session_t *ra_session,
4273 svn_client_ctx_t *ctx,
4274 apr_pool_t *result_pool,
4275 apr_pool_t *scratch_pool)
4277 /* If we haven't already found CHILD->IMPLICIT_MERGEINFO then
4278 contact the server to get it. */
4280 if (child->implicit_mergeinfo)
4281 return SVN_NO_ERROR;
4283 if (child_inherits_parent)
4284 SVN_ERR(inherit_implicit_mergeinfo_from_parent(parent,
4293 SVN_ERR(get_full_mergeinfo(NULL,
4294 &(child->implicit_mergeinfo),
4295 NULL, svn_mergeinfo_inherited,
4296 ra_session, child->abspath,
4297 MAX(revision1, revision2),
4298 MIN(revision1, revision2),
4299 ctx, result_pool, scratch_pool));
4301 return SVN_NO_ERROR;
4304 /* Helper for calculate_remaining_ranges().
4306 Initialize CHILD->REMAINING_RANGES to a rangelist representing the
4307 requested merge of REVISION1:REVISION2 from MERGEINFO_PATH to
4310 For forward merges remove any ranges from CHILD->REMAINING_RANGES that
4311 have already been merged to CHILD->ABSPATH per TARGET_MERGEINFO or
4312 CHILD->IMPLICIT_MERGEINFO. For reverse merges remove any ranges from
4313 CHILD->REMAINING_RANGES that have not already been merged to CHILD->ABSPATH
4314 per TARGET_MERGEINFO or CHILD->IMPLICIT_MERGEINFO. If we have deferred
4315 obtaining CHILD->IMPLICIT_MERGEINFO and it is necessary to use it for
4316 these calculations, then get it from the server, allocating it in
4319 CHILD represents a working copy path which is the merge target or one of
4320 the target's subtrees. If not NULL, PARENT is CHILD's nearest path-wise
4321 ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'.
4323 If the function needs to consider CHILD->IMPLICIT_MERGEINFO and
4324 CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the
4325 mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO. Otherwise contact
4326 the repository for CHILD->IMPLICIT_MERGEINFO.
4328 NOTE: If PARENT is present then this function must have previously been
4329 called for PARENT, i.e. if populate_remaining_ranges() is calling this
4330 function for a set of svn_client__merge_path_t* the calls must be made
4331 in depth-first order.
4333 MERGEINFO_PATH is the merge source relative to the repository root.
4335 REVISION1 and REVISION2 describe the merge range requested from
4338 TARGET_RANGELIST is the portion of CHILD->ABSPATH's explicit or inherited
4339 mergeinfo that intersects with the merge history described by
4340 MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2. TARGET_RANGELIST
4341 should be NULL if there is no explicit or inherited mergeinfo on
4342 CHILD->ABSPATH or an empty list if CHILD->ABSPATH has empty mergeinfo or
4343 explicit mergeinfo that exclusively describes non-intersecting history
4344 with MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2.
4346 SCRATCH_POOL is used for all temporary allocations.
4348 NOTE: This should only be called when honoring mergeinfo.
4350 NOTE: Like calculate_remaining_ranges() if PARENT is present then this
4351 function must have previously been called for PARENT.
4353 static svn_error_t *
4354 filter_merged_revisions(svn_client__merge_path_t *parent,
4355 svn_client__merge_path_t *child,
4356 const char *mergeinfo_path,
4357 svn_rangelist_t *target_rangelist,
4358 svn_revnum_t revision1,
4359 svn_revnum_t revision2,
4360 svn_boolean_t child_inherits_implicit,
4361 svn_ra_session_t *ra_session,
4362 svn_client_ctx_t *ctx,
4363 apr_pool_t *result_pool,
4364 apr_pool_t *scratch_pool)
4366 svn_rangelist_t *requested_rangelist,
4367 *target_implicit_rangelist, *explicit_rangelist;
4369 /* Convert REVISION1 and REVISION2 to a rangelist.
4371 Note: Talking about a requested merge range's inheritability
4372 doesn't make much sense, but as we are using svn_merge_range_t
4373 to describe it we need to pick *something*. Since all the
4374 rangelist manipulations in this function either don't consider
4375 inheritance by default or we are requesting that they don't (i.e.
4376 svn_rangelist_remove and svn_rangelist_intersect) then we could
4377 set the inheritability as FALSE, it won't matter either way. */
4378 requested_rangelist = svn_rangelist__initialize(revision1, revision2,
4379 TRUE, scratch_pool);
4381 /* Now filter out revisions that have already been merged to CHILD. */
4383 if (revision1 > revision2) /* This is a reverse merge. */
4385 svn_rangelist_t *added_rangelist, *deleted_rangelist;
4387 /* The revert range and will need to be reversed for
4388 our svn_rangelist_* APIs to work properly. */
4389 SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
4391 /* Set EXPLICIT_RANGELIST to the list of source-range revs that are
4392 already recorded as merged to target. */
4393 if (target_rangelist)
4395 /* Return the intersection of the revs which are both already
4396 represented by CHILD's explicit or inherited mergeinfo.
4398 We don't consider inheritance when determining intersecting
4399 ranges. If we *did* consider inheritance, then our calculation
4400 would be wrong. For example, if the CHILD->REMAINING_RANGES is
4401 5:3 and TARGET_RANGELIST is r5* (non-inheritable) then the
4402 intersection would be r4. And that would be wrong as we clearly
4403 want to reverse merge both r4 and r5 in this case. Ignoring the
4404 ranges' inheritance results in an intersection of r4-5.
4406 You might be wondering about CHILD's children, doesn't the above
4407 imply that we will reverse merge r4-5 from them? Nope, this is
4408 safe to do because any path whose parent has non-inheritable
4409 ranges is always considered a subtree with differing mergeinfo
4410 even if that path has no explicit mergeinfo prior to the
4411 merge -- See condition 3 in the doc string for
4412 merge.c:get_mergeinfo_paths(). */
4413 SVN_ERR(svn_rangelist_intersect(&explicit_rangelist,
4415 requested_rangelist,
4416 FALSE, scratch_pool));
4420 explicit_rangelist =
4421 apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *));
4424 /* Was any part of the requested reverse merge not accounted for in
4425 CHILD's explicit or inherited mergeinfo? */
4426 SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
4427 requested_rangelist, explicit_rangelist,
4428 FALSE, scratch_pool));
4430 if (deleted_rangelist->nelts == 0)
4432 /* The whole of REVISION1:REVISION2 was represented in CHILD's
4433 explicit/inherited mergeinfo, allocate CHILD's remaining
4434 ranges in POOL and then we are done. */
4435 SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
4436 child->remaining_ranges = svn_rangelist_dup(requested_rangelist,
4439 else /* We need to check CHILD's implicit mergeinfo. */
4441 svn_rangelist_t *implicit_rangelist;
4443 SVN_ERR(ensure_implicit_mergeinfo(parent,
4445 child_inherits_implicit,
4453 target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo,
4456 if (target_implicit_rangelist)
4457 SVN_ERR(svn_rangelist_intersect(&implicit_rangelist,
4458 target_implicit_rangelist,
4459 requested_rangelist,
4460 FALSE, scratch_pool));
4462 implicit_rangelist = apr_array_make(scratch_pool, 0,
4463 sizeof(svn_merge_range_t *));
4465 SVN_ERR(svn_rangelist_merge2(implicit_rangelist,
4466 explicit_rangelist, scratch_pool,
4468 SVN_ERR(svn_rangelist_reverse(implicit_rangelist, scratch_pool));
4469 child->remaining_ranges = svn_rangelist_dup(implicit_rangelist,
4473 else /* This is a forward merge */
4475 /* Set EXPLICIT_RANGELIST to the list of source-range revs that are
4476 NOT already recorded as merged to target. */
4477 if (target_rangelist)
4479 /* See earlier comment preceding svn_rangelist_intersect() for
4480 why we don't consider inheritance here. */
4481 SVN_ERR(svn_rangelist_remove(&explicit_rangelist,
4483 requested_rangelist, FALSE,
4488 explicit_rangelist = svn_rangelist_dup(requested_rangelist,
4492 if (explicit_rangelist->nelts == 0)
4494 child->remaining_ranges =
4495 apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *));
4498 /* ### TODO: Which evil shall we choose?
4500 ### If we allow all forward-merges not already found in recorded
4501 ### mergeinfo, we destroy the ability to, say, merge the whole of a
4502 ### branch to the trunk while automatically ignoring the revisions
4503 ### common to both. That's bad.
4505 ### If we allow only forward-merges not found in either recorded
4506 ### mergeinfo or implicit mergeinfo (natural history), then the
4507 ### previous scenario works great, but we can't reverse-merge a
4508 ### previous change made to our line of history and then remake it
4509 ### (because the reverse-merge will leave no mergeinfo trace, and
4510 ### the remake-it attempt will still find the original change in
4511 ### natural mergeinfo. But you know, that we happen to use 'merge'
4512 ### for revision undoing is somewhat unnatural anyway, so I'm
4513 ### finding myself having little interest in caring too much about
4514 ### this. That said, if we had a way of storing reverse merge
4515 ### ranges, we'd be in good shape either way.
4517 #ifdef SVN_MERGE__ALLOW_ALL_FORWARD_MERGES_FROM_SELF
4519 /* ### Don't consider implicit mergeinfo. */
4520 child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
4525 /* Based on CHILD's TARGET_MERGEINFO there are ranges to merge.
4526 Check CHILD's implicit mergeinfo to see if these remaining
4527 ranges are represented there. */
4528 SVN_ERR(ensure_implicit_mergeinfo(parent,
4530 child_inherits_implicit,
4538 target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo,
4540 if (target_implicit_rangelist)
4541 SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
4542 target_implicit_rangelist,
4544 FALSE, result_pool));
4546 child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
4552 return SVN_NO_ERROR;
4555 /* Helper for do_file_merge and do_directory_merge (by way of
4556 populate_remaining_ranges() for the latter).
4558 Determine what portions of SOURCE have already
4559 been merged to CHILD->ABSPATH and populate CHILD->REMAINING_RANGES with
4560 the ranges that still need merging.
4562 SOURCE and CTX are all cascaded from the caller's arguments of the same
4563 names. Note that this means SOURCE adheres to the requirements noted in
4564 `MERGEINFO MERGE SOURCE NORMALIZATION'.
4566 CHILD represents a working copy path which is the merge target or one of
4567 the target's subtrees. If not NULL, PARENT is CHILD's nearest path-wise
4568 ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. TARGET_MERGEINFO is
4569 the working mergeinfo on CHILD.
4571 RA_SESSION is the session for the younger of SOURCE->loc1 and
4574 If the function needs to consider CHILD->IMPLICIT_MERGEINFO and
4575 CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the
4576 mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO. Otherwise contact
4577 the repository for CHILD->IMPLICIT_MERGEINFO.
4579 If not null, IMPLICIT_SRC_GAP is the gap, if any, in the natural history
4580 of SOURCE, see merge_cmd_baton_t.implicit_src_gap.
4582 SCRATCH_POOL is used for all temporary allocations. Changes to CHILD and
4583 PARENT are made in RESULT_POOL.
4585 NOTE: This should only be called when honoring mergeinfo.
4587 NOTE: If PARENT is present then this function must have previously been
4588 called for PARENT, i.e. if populate_remaining_ranges() is calling this
4589 function for a set of svn_client__merge_path_t* the calls must be made
4590 in depth-first order.
4592 NOTE: When performing reverse merges, return
4593 SVN_ERR_CLIENT_NOT_READY_TO_MERGE if both locations in SOURCE and
4594 CHILD->ABSPATH are all on the same line of history but CHILD->ABSPATH's
4595 base revision is older than the SOURCE->rev1:rev2 range, see comment re
4598 static svn_error_t *
4599 calculate_remaining_ranges(svn_client__merge_path_t *parent,
4600 svn_client__merge_path_t *child,
4601 const merge_source_t *source,
4602 svn_mergeinfo_t target_mergeinfo,
4603 const apr_array_header_t *implicit_src_gap,
4604 svn_boolean_t child_inherits_implicit,
4605 svn_ra_session_t *ra_session,
4606 svn_client_ctx_t *ctx,
4607 apr_pool_t *result_pool,
4608 apr_pool_t *scratch_pool)
4610 const svn_client__pathrev_t *primary_src
4611 = (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1;
4612 const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
4614 /* Intersection of TARGET_MERGEINFO and the merge history
4615 described by SOURCE. */
4616 svn_rangelist_t *target_rangelist;
4617 svn_revnum_t child_base_revision;
4619 /* Since this function should only be called when honoring mergeinfo and
4620 * SOURCE adheres to the requirements noted in 'MERGEINFO MERGE SOURCE
4621 * NORMALIZATION', SOURCE must be 'ancestral'. */
4622 SVN_ERR_ASSERT(source->ancestral);
4624 /* Determine which of the requested ranges to consider merging... */
4626 /* Set TARGET_RANGELIST to the portion of TARGET_MERGEINFO that refers
4627 to SOURCE (excluding any gap in SOURCE): first get all ranges from
4628 TARGET_MERGEINFO that refer to the path of SOURCE, and then prune
4629 any ranges that lie in the gap in SOURCE.
4631 ### [JAF] In fact, that may still leave some ranges that lie entirely
4632 outside the range of SOURCE; it seems we don't care about that. */
4633 if (target_mergeinfo)
4634 target_rangelist = svn_hash_gets(target_mergeinfo, mergeinfo_path);
4636 target_rangelist = NULL;
4637 if (implicit_src_gap && target_rangelist)
4639 /* Remove any mergeinfo referring to the 'gap' in SOURCE, as that
4640 mergeinfo doesn't really refer to SOURCE at all but instead
4641 refers to locations that are non-existent or on a different
4642 line of history. (Issue #3242.) */
4643 SVN_ERR(svn_rangelist_remove(&target_rangelist,
4644 implicit_src_gap, target_rangelist,
4645 FALSE, result_pool));
4648 /* Initialize CHILD->REMAINING_RANGES and filter out revisions already
4649 merged (or, in the case of reverse merges, ranges not yet merged). */
4650 SVN_ERR(filter_merged_revisions(parent, child, mergeinfo_path,
4652 source->loc1->rev, source->loc2->rev,
4653 child_inherits_implicit,
4654 ra_session, ctx, result_pool,
4657 /* Issue #2973 -- from the continuing series of "Why, since the advent of
4658 merge tracking, allowing merges into mixed rev and locally modified
4659 working copies isn't simple and could be considered downright evil".
4661 If reverse merging a range to the WC path represented by CHILD, from
4662 that path's own history, where the path inherits no locally modified
4663 mergeinfo from its WC parents (i.e. there is no uncommitted merge to
4664 the WC), and the path's base revision is older than the range, then
4665 the merge will always be a no-op. This is because we only allow reverse
4666 merges of ranges in the path's explicit or natural mergeinfo and a
4667 reverse merge from the path's future history obviously isn't going to be
4668 in either, hence the no-op.
4670 The problem is two-fold. First, in a mixed rev WC, the change we
4671 want to revert might actually be to some child of the target path
4672 which is at a younger base revision. Sure, we can merge directly
4673 to that child or update the WC or even use --ignore-ancestry and then
4674 successfully run the reverse merge, but that gets to the second
4675 problem: Those courses of action are not very obvious. Before 1.5 if
4676 a user committed a change that didn't touch the commit target, then
4677 immediately decided to revert that change via a reverse merge it would
4678 just DTRT. But with the advent of merge tracking the user gets a no-op.
4680 So in the name of user friendliness, return an error suggesting a helpful
4683 SVN_ERR(svn_wc__node_get_base(NULL, &child_base_revision,
4684 NULL, NULL, NULL, NULL,
4685 ctx->wc_ctx, child->abspath,
4686 TRUE /* ignore_enoent */,
4687 FALSE /* show_hidden */,
4688 scratch_pool, scratch_pool));
4689 /* If CHILD has no base revision then it hasn't been committed yet, so it
4690 can't have any "future" history. */
4691 if (SVN_IS_VALID_REVNUM(child_base_revision)
4692 && ((child->remaining_ranges)->nelts == 0) /* Inoperative merge */
4693 && (source->loc2->rev < source->loc1->rev) /* Reverse merge */
4694 && (child_base_revision <= source->loc2->rev)) /* From CHILD's future */
4696 /* Hmmm, an inoperative reverse merge from the "future". If it is
4697 from our own future return a helpful error. */
4699 svn_client__pathrev_t *start_loc;
4701 err = svn_client__repos_location(&start_loc,
4704 child_base_revision,
4705 ctx, scratch_pool, scratch_pool);
4708 if (err->apr_err == SVN_ERR_FS_NOT_FOUND
4709 || err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
4710 svn_error_clear(err);
4712 return svn_error_trace(err);
4718 SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, child->abspath,
4719 scratch_pool, scratch_pool));
4720 if (strcmp(start_loc->url, url) == 0)
4721 return svn_error_create(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
4722 _("Cannot reverse-merge a range from a "
4723 "path's own future history; try "
4728 return SVN_NO_ERROR;
4731 /* Helper for populate_remaining_ranges().
4733 SOURCE is cascaded from the arguments of the same name in
4734 populate_remaining_ranges().
4736 Note: The following comments assume a forward merge, i.e.
4737 SOURCE->loc1->rev < SOURCE->loc2->rev. If this is a reverse merge then
4738 all the following comments still apply, but with SOURCE->loc1 switched
4741 Like populate_remaining_ranges(), SOURCE must adhere to the restrictions
4742 documented in 'MERGEINFO MERGE SOURCE NORMALIZATION'. These restrictions
4743 allow for a *single* gap in SOURCE, GAP_REV1:GAP_REV2 exclusive:inclusive
4744 (where SOURCE->loc1->rev == GAP_REV1 <= GAP_REV2 < SOURCE->loc2->rev),
4745 if SOURCE->loc2->url@(GAP_REV2+1) was copied from SOURCE->loc1. If such
4746 a gap exists, set *GAP_START and *GAP_END to the starting and ending
4747 revisions of the gap. Otherwise set both to SVN_INVALID_REVNUM.
4749 For example, if the natural history of URL@2:URL@9 is 'trunk/:2,7-9' this
4750 would indicate that trunk@7 was copied from trunk@2. This function would
4751 return GAP_START:GAP_END of 2:6 in this case. Note that a path 'trunk'
4752 might exist at r3-6, but it would not be on the same line of history as
4755 ### GAP_START is basically redundant, as (if there is a gap at all) it is
4756 necessarily the older revision of SOURCE.
4758 RA_SESSION is an open RA session to the repository in which SOURCE lives.
4760 static svn_error_t *
4761 find_gaps_in_merge_source_history(svn_revnum_t *gap_start,
4762 svn_revnum_t *gap_end,
4763 const merge_source_t *source,
4764 svn_ra_session_t *ra_session,
4765 svn_client_ctx_t *ctx,
4766 apr_pool_t *scratch_pool)
4768 svn_mergeinfo_t implicit_src_mergeinfo;
4769 svn_revnum_t old_rev = MIN(source->loc1->rev, source->loc2->rev);
4770 const svn_client__pathrev_t *primary_src
4771 = (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1;
4772 const char *merge_src_fspath = svn_client__pathrev_fspath(primary_src,
4774 svn_rangelist_t *rangelist;
4776 SVN_ERR_ASSERT(source->ancestral);
4778 /* Start by assuming there is no gap. */
4779 *gap_start = *gap_end = SVN_INVALID_REVNUM;
4781 /* Easy out: There can't be a gap between adjacent revisions. */
4782 if (abs(source->loc1->rev - source->loc2->rev) == 1)
4783 return SVN_NO_ERROR;
4785 /* Get SOURCE as mergeinfo. */
4786 SVN_ERR(svn_client__get_history_as_mergeinfo(&implicit_src_mergeinfo, NULL,
4788 primary_src->rev, old_rev,
4790 ctx, scratch_pool));
4792 rangelist = svn_hash_gets(implicit_src_mergeinfo, merge_src_fspath);
4794 if (!rangelist) /* ### Can we ever not find a rangelist? */
4795 return SVN_NO_ERROR;
4797 /* A gap in natural history can result from either a copy or
4798 a rename. If from a copy then history as mergeinfo will look
4799 something like this:
4803 If from a rename it will look like this:
4806 '/trunk_new_name:Y-Z'
4808 In both cases the gap, if it exists, is M-N, where M = X + 1 and
4811 Note that per the rules of 'MERGEINFO MERGE SOURCE NORMALIZATION' we
4812 should never have multiple gaps, e.g. if we see anything like the
4813 following then something is quite wrong:
4815 '/trunk_old_name:A,B-C'
4816 '/trunk_new_name:D-E'
4819 if (rangelist->nelts > 1) /* Copy */
4821 const svn_merge_range_t *gap;
4822 /* As mentioned above, multiple gaps *shouldn't* be possible. */
4823 SVN_ERR_ASSERT(apr_hash_count(implicit_src_mergeinfo) == 1);
4825 gap = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
4826 const svn_merge_range_t *);
4828 *gap_start = MIN(source->loc1->rev, source->loc2->rev);
4829 *gap_end = gap->start;
4832 ### This assertion triggers in merge_tests.py svnmucc_abuse_1()
4833 ### when a node is replaced by an older copy of itself.
4835 BH: I think we should review this and the 'rename' case to find
4836 out which behavior we really want, and if we can really
4837 determine what happened this way. */
4838 SVN_ERR_ASSERT(*gap_start < *gap_end);
4840 else if (apr_hash_count(implicit_src_mergeinfo) > 1) /* Rename */
4842 svn_rangelist_t *requested_rangelist =
4843 svn_rangelist__initialize(MIN(source->loc1->rev, source->loc2->rev),
4844 MAX(source->loc1->rev, source->loc2->rev),
4845 TRUE, scratch_pool);
4846 svn_rangelist_t *implicit_rangelist =
4847 apr_array_make(scratch_pool, 2, sizeof(svn_merge_range_t *));
4848 svn_rangelist_t *gap_rangelist;
4850 SVN_ERR(svn_rangelist__merge_many(implicit_rangelist,
4851 implicit_src_mergeinfo,
4852 scratch_pool, scratch_pool));
4853 SVN_ERR(svn_rangelist_remove(&gap_rangelist, implicit_rangelist,
4854 requested_rangelist, FALSE,
4857 /* If there is anything left it is the gap. */
4858 if (gap_rangelist->nelts)
4860 svn_merge_range_t *gap_range =
4861 APR_ARRAY_IDX(gap_rangelist, 0, svn_merge_range_t *);
4863 *gap_start = gap_range->start;
4864 *gap_end = gap_range->end;
4868 SVN_ERR_ASSERT(*gap_start == MIN(source->loc1->rev, source->loc2->rev)
4869 || (*gap_start == SVN_INVALID_REVNUM
4870 && *gap_end == SVN_INVALID_REVNUM));
4871 return SVN_NO_ERROR;
4874 /* Helper for do_directory_merge().
4876 For each (svn_client__merge_path_t *) child in CHILDREN_WITH_MERGEINFO,
4877 populate that child's 'remaining_ranges' list with (### ... what?),
4878 and populate that child's 'implicit_mergeinfo' with its implicit
4879 mergeinfo (natural history). CHILDREN_WITH_MERGEINFO is expected
4880 to be sorted in depth first order and each child must be processed in
4881 that order. The inheritability of all calculated ranges is TRUE.
4883 If mergeinfo is being honored (based on MERGE_B -- see HONOR_MERGEINFO()
4884 for how this is determined), this function will actually try to be
4885 intelligent about populating remaining_ranges list. Otherwise, it
4886 will claim that each child has a single remaining range, from
4887 SOURCE->rev1, to SOURCE->rev2.
4888 ### We also take the short-cut if doing record-only. Why?
4890 SCRATCH_POOL is used for all temporary allocations. Changes to
4891 CHILDREN_WITH_MERGEINFO are made in RESULT_POOL.
4893 Note that if SOURCE->rev1 > SOURCE->rev2, then each child's remaining_ranges
4894 member does not adhere to the API rules for rangelists described in
4895 svn_mergeinfo.h -- See svn_client__merge_path_t.
4897 See `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements
4900 static svn_error_t *
4901 populate_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
4902 const merge_source_t *source,
4903 svn_ra_session_t *ra_session,
4904 merge_cmd_baton_t *merge_b,
4905 apr_pool_t *result_pool,
4906 apr_pool_t *scratch_pool)
4908 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4910 svn_revnum_t gap_start, gap_end;
4912 /* If we aren't honoring mergeinfo or this is a --record-only merge,
4913 we'll make quick work of this by simply adding dummy SOURCE->rev1:rev2
4914 ranges for all children. */
4915 if (! HONOR_MERGEINFO(merge_b) || merge_b->record_only)
4917 for (i = 0; i < children_with_mergeinfo->nelts; i++)
4919 svn_client__merge_path_t *child =
4920 APR_ARRAY_IDX(children_with_mergeinfo, i,
4921 svn_client__merge_path_t *);
4923 svn_pool_clear(iterpool);
4925 /* Issue #3646 'record-only merges create self-referential
4926 mergeinfo'. Get the merge target's implicit mergeinfo (natural
4927 history). We'll use it later to avoid setting self-referential
4928 mergeinfo -- see filter_natural_history_from_mergeinfo(). */
4929 if (i == 0) /* First item is always the merge target. */
4931 SVN_ERR(get_full_mergeinfo(NULL, /* child->pre_merge_mergeinfo */
4932 &(child->implicit_mergeinfo),
4933 NULL, /* child->inherited_mergeinfo */
4934 svn_mergeinfo_inherited, ra_session,
4936 MAX(source->loc1->rev,
4938 MIN(source->loc1->rev,
4940 merge_b->ctx, result_pool,
4945 /* Issue #3443 - Subtrees of the merge target can inherit
4946 their parent's implicit mergeinfo in most cases. */
4947 svn_client__merge_path_t *parent
4948 = find_nearest_ancestor(children_with_mergeinfo,
4949 FALSE, child->abspath);
4950 svn_boolean_t child_inherits_implicit;
4952 /* If CHILD is a subtree then its parent must be in
4953 CHILDREN_WITH_MERGEINFO, see the global comment
4954 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
4955 SVN_ERR_ASSERT(parent);
4957 child_inherits_implicit = (parent && !child->switched);
4958 SVN_ERR(ensure_implicit_mergeinfo(parent, child,
4959 child_inherits_implicit,
4962 ra_session, merge_b->ctx,
4963 result_pool, iterpool));
4966 child->remaining_ranges = svn_rangelist__initialize(source->loc1->rev,
4971 svn_pool_destroy(iterpool);
4972 return SVN_NO_ERROR;
4975 /* If, in the merge source's history, there was a copy from an older
4976 revision, then SOURCE->loc2->url won't exist at some range M:N, where
4977 SOURCE->loc1->rev < M < N < SOURCE->loc2->rev. The rules of 'MERGEINFO
4978 MERGE SOURCE NORMALIZATION' allow this, but we must ignore these gaps
4979 when calculating what ranges remain to be merged from SOURCE. If we
4980 don't and try to merge any part of SOURCE->loc2->url@M:N we would
4981 break the editor since no part of that actually exists. See
4982 http://svn.haxx.se/dev/archive-2008-11/0618.shtml.
4984 Find the gaps in the merge target's history, if any. Eventually
4985 we will adjust CHILD->REMAINING_RANGES such that we don't describe
4986 non-existent paths to the editor. */
4987 SVN_ERR(find_gaps_in_merge_source_history(&gap_start, &gap_end,
4989 ra_session, merge_b->ctx,
4992 /* Stash any gap in the merge command baton, we'll need it later when
4993 recording mergeinfo describing this merge. */
4994 if (SVN_IS_VALID_REVNUM(gap_start) && SVN_IS_VALID_REVNUM(gap_end))
4995 merge_b->implicit_src_gap = svn_rangelist__initialize(gap_start, gap_end,
4998 for (i = 0; i < children_with_mergeinfo->nelts; i++)
5000 svn_client__merge_path_t *child =
5001 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5002 const char *child_repos_path
5003 = svn_dirent_skip_ancestor(merge_b->target->abspath, child->abspath);
5004 merge_source_t child_source;
5005 svn_client__merge_path_t *parent = NULL;
5006 svn_boolean_t child_inherits_implicit;
5008 svn_pool_clear(iterpool);
5010 /* If the path is absent don't do subtree merge either. */
5011 SVN_ERR_ASSERT(child);
5015 SVN_ERR_ASSERT(child_repos_path != NULL);
5016 child_source.loc1 = svn_client__pathrev_join_relpath(
5017 source->loc1, child_repos_path, iterpool);
5018 child_source.loc2 = svn_client__pathrev_join_relpath(
5019 source->loc2, child_repos_path, iterpool);
5020 /* ### Is the child 'ancestral' over the same revision range? It's
5021 * not necessarily true that a child is 'ancestral' if the parent is,
5022 * nor that it's not if the parent is not. However, here we claim
5023 * that it is. Before we had this 'ancestral' field that we need to
5024 * set explicitly, the claim was implicit. Either way, the impact is
5025 * that we might pass calculate_remaining_ranges() a source that is
5026 * not in fact 'ancestral' (despite its 'ancestral' field being true),
5027 * contrary to its doc-string. */
5028 child_source.ancestral = source->ancestral;
5030 /* Get the explicit/inherited mergeinfo for CHILD. If CHILD is the
5031 merge target then also get its implicit mergeinfo. Otherwise defer
5032 this until we know it is absolutely necessary, since it requires an
5033 expensive round trip communication with the server. */
5034 SVN_ERR(get_full_mergeinfo(
5035 child->pre_merge_mergeinfo ? NULL : &(child->pre_merge_mergeinfo),
5036 /* Get implicit only for merge target. */
5037 (i == 0) ? &(child->implicit_mergeinfo) : NULL,
5038 &(child->inherited_mergeinfo),
5039 svn_mergeinfo_inherited, ra_session,
5041 MAX(source->loc1->rev, source->loc2->rev),
5042 MIN(source->loc1->rev, source->loc2->rev),
5043 merge_b->ctx, result_pool, iterpool));
5045 /* If CHILD isn't the merge target find its parent. */
5048 parent = find_nearest_ancestor(children_with_mergeinfo,
5049 FALSE, child->abspath);
5050 /* If CHILD is a subtree then its parent must be in
5051 CHILDREN_WITH_MERGEINFO, see the global comment
5052 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
5053 SVN_ERR_ASSERT(parent);
5056 /* Issue #3443 - Can CHILD inherit PARENT's implicit mergeinfo, saving
5057 us from having to ask the repos? The only time we can't do this is if
5058 CHILD is the merge target and so there is no PARENT to inherit from
5059 or if CHILD is the root of a switched subtree, in which case PARENT
5060 exists but is not CHILD's repository parent. */
5061 child_inherits_implicit = (parent && !child->switched);
5063 SVN_ERR(calculate_remaining_ranges(parent, child,
5065 child->pre_merge_mergeinfo,
5066 merge_b->implicit_src_gap,
5067 child_inherits_implicit,
5069 merge_b->ctx, result_pool,
5072 /* Deal with any gap in SOURCE's natural history.
5074 If the gap is a proper subset of CHILD->REMAINING_RANGES then we can
5075 safely ignore it since we won't describe this path/rev pair.
5077 If the gap exactly matches or is a superset of a range in
5078 CHILD->REMAINING_RANGES then we must remove that range so we don't
5079 attempt to describe non-existent paths via the reporter, this will
5080 break the editor and our merge.
5082 If the gap adjoins or overlaps a range in CHILD->REMAINING_RANGES
5083 then we must *add* the gap so we span the missing revisions. */
5084 if (child->remaining_ranges->nelts
5085 && merge_b->implicit_src_gap)
5088 svn_boolean_t proper_subset = FALSE;
5089 svn_boolean_t overlaps_or_adjoins = FALSE;
5091 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
5092 so it will work with the svn_rangelist_* APIs below. */
5093 if (source->loc1->rev > source->loc2->rev)
5094 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
5096 for (j = 0; j < child->remaining_ranges->nelts; j++)
5098 svn_merge_range_t *range
5099 = APR_ARRAY_IDX(child->remaining_ranges, j, svn_merge_range_t *);
5101 if ((range->start <= gap_start && gap_end < range->end)
5102 || (range->start < gap_start && gap_end <= range->end))
5104 proper_subset = TRUE;
5107 else if ((gap_start == range->start) && (range->end == gap_end))
5111 else if (gap_start <= range->end && range->start <= gap_end)
5114 overlaps_or_adjoins = TRUE;
5121 /* We need to make adjustments. Remove from, or add the gap
5122 to, CHILD->REMAINING_RANGES as appropriate. */
5124 if (overlaps_or_adjoins)
5125 SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
5126 merge_b->implicit_src_gap,
5127 result_pool, iterpool));
5128 else /* equals == TRUE */
5129 SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
5130 merge_b->implicit_src_gap,
5131 child->remaining_ranges, FALSE,
5135 if (source->loc1->rev > source->loc2->rev) /* Reverse merge */
5136 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
5140 svn_pool_destroy(iterpool);
5141 return SVN_NO_ERROR;
5145 /*-----------------------------------------------------------------------*/
5147 /*** Other Helper Functions ***/
5149 /* Calculate the new mergeinfo for the target tree rooted at TARGET_ABSPATH
5150 based on MERGES (a mapping of absolute WC paths to rangelists representing
5151 a merge from the source SOURCE_FSPATH).
5153 If RESULT_CATALOG is NULL, then record the new mergeinfo in the WC (at,
5154 and possibly below, TARGET_ABSPATH).
5156 If RESULT_CATALOG is not NULL, then don't record the new mergeinfo on the
5157 WC, but instead record it in RESULT_CATALOG, where the keys are absolute
5158 working copy paths and the values are the new mergeinfos for each.
5159 Allocate additions to RESULT_CATALOG in pool which RESULT_CATALOG was
5161 static svn_error_t *
5162 update_wc_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
5163 const char *target_abspath,
5164 const char *source_fspath,
5166 svn_boolean_t is_rollback,
5167 svn_client_ctx_t *ctx,
5168 apr_pool_t *scratch_pool)
5170 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5171 apr_hash_index_t *hi;
5173 /* Combine the mergeinfo for the revision range just merged into
5174 the WC with its on-disk mergeinfo. */
5175 for (hi = apr_hash_first(scratch_pool, merges); hi; hi = apr_hash_next(hi))
5177 const char *local_abspath = svn__apr_hash_index_key(hi);
5178 svn_rangelist_t *ranges = svn__apr_hash_index_val(hi);
5179 svn_rangelist_t *rangelist;
5181 const char *local_abspath_rel_to_target;
5183 svn_mergeinfo_t mergeinfo;
5185 svn_pool_clear(iterpool);
5187 /* As some of the merges may've changed the WC's mergeinfo, get
5188 a fresh copy before using it to update the WC's mergeinfo. */
5189 err = svn_client__parse_mergeinfo(&mergeinfo, ctx->wc_ctx,
5190 local_abspath, iterpool, iterpool);
5192 /* If a directory PATH was skipped because it is missing or was
5193 obstructed by an unversioned item then there's nothing we can
5194 do with that, so skip it. */
5197 if (err->apr_err == SVN_ERR_WC_NOT_LOCKED
5198 || err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5200 svn_error_clear(err);
5205 return svn_error_trace(err);
5209 /* If we are attempting to set empty revision range override mergeinfo
5210 on a path with no explicit mergeinfo, we first need the
5211 mergeinfo that path inherits. */
5212 if (mergeinfo == NULL && ranges->nelts == 0)
5214 SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, NULL,
5215 svn_mergeinfo_nearest_ancestor,
5216 local_abspath, NULL, NULL,
5217 FALSE, ctx, iterpool, iterpool));
5220 if (mergeinfo == NULL)
5221 mergeinfo = apr_hash_make(iterpool);
5223 local_abspath_rel_to_target = svn_dirent_skip_ancestor(target_abspath,
5225 SVN_ERR_ASSERT(local_abspath_rel_to_target != NULL);
5226 fspath = svn_fspath__join(source_fspath,
5227 local_abspath_rel_to_target,
5229 rangelist = svn_hash_gets(mergeinfo, fspath);
5230 if (rangelist == NULL)
5231 rangelist = apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
5235 ranges = svn_rangelist_dup(ranges, iterpool);
5236 SVN_ERR(svn_rangelist_reverse(ranges, iterpool));
5237 SVN_ERR(svn_rangelist_remove(&rangelist, ranges, rangelist,
5243 SVN_ERR(svn_rangelist_merge2(rangelist, ranges, iterpool, iterpool));
5245 /* Update the mergeinfo by adjusting the path's rangelist. */
5246 svn_hash_sets(mergeinfo, fspath, rangelist);
5248 if (is_rollback && apr_hash_count(mergeinfo) == 0)
5251 svn_mergeinfo__remove_empty_rangelists(mergeinfo, scratch_pool);
5255 svn_mergeinfo_t existing_mergeinfo =
5256 svn_hash_gets(result_catalog, local_abspath);
5257 apr_pool_t *result_catalog_pool = apr_hash_pool_get(result_catalog);
5259 if (existing_mergeinfo)
5260 SVN_ERR(svn_mergeinfo_merge2(mergeinfo, existing_mergeinfo,
5261 result_catalog_pool, scratch_pool));
5262 svn_hash_sets(result_catalog,
5263 apr_pstrdup(result_catalog_pool, local_abspath),
5264 svn_mergeinfo_dup(mergeinfo, result_catalog_pool));
5268 err = svn_client__record_wc_mergeinfo(local_abspath, mergeinfo,
5269 TRUE, ctx, iterpool);
5271 if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
5273 /* PATH isn't just missing, it's not even versioned as far
5274 as this working copy knows. But it was included in
5275 MERGES, which means that the server knows about it.
5276 Likely we don't have access to the source due to authz
5277 restrictions. For now just clear the error and
5280 ### TODO: Set non-inheritable mergeinfo on PATH's immediate
5281 ### parent and normal mergeinfo on PATH's siblings which we
5282 ### do have access to. */
5283 svn_error_clear(err);
5290 svn_pool_destroy(iterpool);
5291 return SVN_NO_ERROR;
5294 /* Helper for record_mergeinfo_for_dir_merge().
5296 Record override mergeinfo on any paths skipped during a merge.
5298 Set empty mergeinfo on each path in MERGE_B->SKIPPED_ABSPATHS so the path
5299 does not incorrectly inherit mergeinfo that will later be describing
5302 MERGEINFO_PATH and MERGE_B are cascaded from
5303 arguments of the same name in the caller.
5305 IS_ROLLBACK is true if the caller is recording a reverse merge and false
5306 otherwise. RANGELIST is the set of revisions being merged from
5307 MERGEINFO_PATH to MERGE_B->target. */
5308 static svn_error_t *
5309 record_skips_in_mergeinfo(const char *mergeinfo_path,
5310 const svn_rangelist_t *rangelist,
5311 svn_boolean_t is_rollback,
5312 merge_cmd_baton_t *merge_b,
5313 apr_pool_t *scratch_pool)
5315 apr_hash_index_t *hi;
5317 apr_size_t nbr_skips = apr_hash_count(merge_b->skipped_abspaths);
5318 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5321 return SVN_NO_ERROR;
5323 merges = apr_hash_make(scratch_pool);
5325 /* Override the mergeinfo for child paths which weren't actually merged. */
5326 for (hi = apr_hash_first(scratch_pool, merge_b->skipped_abspaths); hi;
5327 hi = apr_hash_next(hi))
5329 const char *skipped_abspath = svn__apr_hash_index_key(hi);
5330 svn_wc_notify_state_t obstruction_state;
5332 svn_pool_clear(iterpool);
5334 /* Before we override, make sure this is a versioned path, it might
5335 be an external or missing from disk due to authz restrictions. */
5336 SVN_ERR(perform_obstruction_check(&obstruction_state, NULL, NULL,
5338 merge_b, skipped_abspath,
5340 if (obstruction_state == svn_wc_notify_state_obstructed
5341 || obstruction_state == svn_wc_notify_state_missing)
5344 /* Add an empty range list for this path.
5346 ### TODO: This works fine for a file path skipped because it is
5347 ### missing as long as the file's parent directory is present.
5348 ### But missing directory paths skipped are not handled yet,
5349 ### see issue #2915.
5351 ### TODO: An empty range is fine if the skipped path doesn't
5352 ### inherit any mergeinfo from a parent, but if it does
5353 ### we need to account for that. See issue #3440
5354 ### http://subversion.tigris.org/issues/show_bug.cgi?id=3440. */
5355 svn_hash_sets(merges, skipped_abspath,
5356 apr_array_make(scratch_pool, 0,
5357 sizeof(svn_merge_range_t *)));
5359 /* if (nbr_skips < notify_b->nbr_notifications)
5360 ### Use RANGELIST as the mergeinfo for all children of
5361 ### this path which were not also explicitly
5364 SVN_ERR(update_wc_mergeinfo(NULL, merge_b->target->abspath,
5365 mergeinfo_path, merges,
5366 is_rollback, merge_b->ctx, iterpool));
5367 svn_pool_destroy(iterpool);
5368 return SVN_NO_ERROR;
5371 /* Data for reporting when a merge aborted because of raising conflicts.
5373 typedef struct single_range_conflict_report_t
5375 /* What sub-range of the requested source raised conflicts?
5376 * The 'inheritable' flag is ignored. */
5377 merge_source_t *conflicted_range;
5378 /* What sub-range of the requested source remains to be merged?
5379 * NULL if no more. The 'inheritable' flag is ignored. */
5380 merge_source_t *remaining_source;
5382 } single_range_conflict_report_t;
5384 /* Create a single_range_conflict_report_t, containing deep copies of
5385 * CONFLICTED_RANGE and REMAINING_SOURCE, allocated in RESULT_POOL. */
5386 static single_range_conflict_report_t *
5387 single_range_conflict_report_create(const merge_source_t *conflicted_range,
5388 const merge_source_t *remaining_source,
5389 apr_pool_t *result_pool)
5391 single_range_conflict_report_t *report
5392 = apr_palloc(result_pool, sizeof(*report));
5394 assert(conflicted_range != NULL);
5396 report->conflicted_range = merge_source_dup(conflicted_range, result_pool);
5397 report->remaining_source
5398 = remaining_source ? merge_source_dup(remaining_source, result_pool)
5403 /* Data for reporting when a merge aborted because of raising conflicts.
5405 * ### TODO: More info, including the ranges (or other parameters) the user
5406 * needs to complete the merge.
5408 typedef struct conflict_report_t
5410 const char *target_abspath;
5411 /* The revision range during which conflicts were raised */
5412 const merge_source_t *conflicted_range;
5413 /* Was the conflicted range the last range in the whole requested merge? */
5414 svn_boolean_t was_last_range;
5415 } conflict_report_t;
5417 /* Return a new conflict_report_t containing deep copies of the parameters,
5418 * allocated in RESULT_POOL. */
5419 static conflict_report_t *
5420 conflict_report_create(const char *target_abspath,
5421 const merge_source_t *conflicted_range,
5422 svn_boolean_t was_last_range,
5423 apr_pool_t *result_pool)
5425 conflict_report_t *report = apr_palloc(result_pool, sizeof(*report));
5427 report->target_abspath = apr_pstrdup(result_pool, target_abspath);
5428 report->conflicted_range = merge_source_dup(conflicted_range, result_pool);
5429 report->was_last_range = was_last_range;
5433 /* Return a deep copy of REPORT, allocated in RESULT_POOL. */
5434 static conflict_report_t *
5435 conflict_report_dup(const conflict_report_t *report,
5436 apr_pool_t *result_pool)
5438 conflict_report_t *new = apr_pmemdup(result_pool, report, sizeof(*new));
5440 new->target_abspath = apr_pstrdup(result_pool, report->target_abspath);
5441 new->conflicted_range = merge_source_dup(report->conflicted_range,
5446 /* Create and return an error structure appropriate for the unmerged
5447 revisions range(s). */
5448 static APR_INLINE svn_error_t *
5449 make_merge_conflict_error(conflict_report_t *report,
5450 apr_pool_t *scratch_pool)
5452 assert(!report || svn_dirent_is_absolute(report->target_abspath));
5454 if (report && ! report->was_last_range)
5456 svn_error_t *err = svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
5457 _("One or more conflicts were produced while merging r%ld:%ld into\n"
5459 "resolve all conflicts and rerun the merge to apply the remaining\n"
5460 "unmerged revisions"),
5461 report->conflicted_range->loc1->rev, report->conflicted_range->loc2->rev,
5462 svn_dirent_local_style(report->target_abspath, scratch_pool));
5463 assert(report->conflicted_range->loc1->rev != report->conflicted_range->loc2->rev); /* ### is a valid case in a 2-URL merge */
5466 return SVN_NO_ERROR;
5469 /* Helper for do_directory_merge().
5471 TARGET_WCPATH is a directory and CHILDREN_WITH_MERGEINFO is filled
5472 with paths (svn_client__merge_path_t *) arranged in depth first order,
5473 which have mergeinfo set on them or meet one of the other criteria
5474 defined in get_mergeinfo_paths(). Remove any paths absent from disk
5475 or scheduled for deletion from CHILDREN_WITH_MERGEINFO which are equal to
5476 or are descendants of TARGET_WCPATH by setting those children to NULL. */
5478 remove_absent_children(const char *target_wcpath,
5479 apr_array_header_t *children_with_mergeinfo)
5481 /* Before we try to override mergeinfo for skipped paths, make sure
5482 the path isn't absent due to authz restrictions, because there's
5483 nothing we can do about those. */
5485 for (i = 0; i < children_with_mergeinfo->nelts; i++)
5487 svn_client__merge_path_t *child =
5488 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5489 if ((child->absent || child->scheduled_for_deletion)
5490 && svn_dirent_is_ancestor(target_wcpath, child->abspath))
5492 svn_sort__array_delete(children_with_mergeinfo, i--, 1);
5497 /* Helper for do_directory_merge() to handle the case where a merge editor
5498 drive removes explicit mergeinfo from a subtree of the merge target.
5500 MERGE_B is cascaded from the argument of the same name in
5501 do_directory_merge(). For each path (if any) in
5502 MERGE_B->PATHS_WITH_DELETED_MERGEINFO remove that path from
5503 CHILDREN_WITH_MERGEINFO.
5505 The one exception is for the merge target itself,
5506 MERGE_B->target->abspath, this must always be present in
5507 CHILDREN_WITH_MERGEINFO so this is never removed by this
5510 remove_children_with_deleted_mergeinfo(merge_cmd_baton_t *merge_b,
5511 apr_array_header_t *children_with_mergeinfo)
5515 if (!merge_b->paths_with_deleted_mergeinfo)
5518 /* CHILDREN_WITH_MERGEINFO[0] is the always the merge target
5519 so start at the first child. */
5520 for (i = 1; i < children_with_mergeinfo->nelts; i++)
5522 svn_client__merge_path_t *child =
5523 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5525 if (svn_hash_gets(merge_b->paths_with_deleted_mergeinfo, child->abspath))
5527 svn_sort__array_delete(children_with_mergeinfo, i--, 1);
5532 /* Helper for do_directory_merge().
5534 Set up the diff editor report to merge the SOURCE diff
5535 into TARGET_ABSPATH and drive it.
5537 If mergeinfo is not being honored (based on MERGE_B -- see the doc
5538 string for HONOR_MERGEINFO() for how this is determined), then ignore
5539 CHILDREN_WITH_MERGEINFO and merge the SOURCE diff to TARGET_ABSPATH.
5541 If mergeinfo is being honored then perform a history-aware merge,
5542 describing TARGET_ABSPATH and its subtrees to the reporter in such as way
5543 as to avoid repeating merges already performed per the mergeinfo and
5544 natural history of TARGET_ABSPATH and its subtrees.
5546 The ranges that still need to be merged to the TARGET_ABSPATH and its
5547 subtrees are described in CHILDREN_WITH_MERGEINFO, an array of
5548 svn_client__merge_path_t * -- see 'THE CHILDREN_WITH_MERGEINFO ARRAY'
5549 comment at the top of this file for more info. Note that it is possible
5550 TARGET_ABSPATH and/or some of its subtrees need only a subset, or no part,
5551 of SOURCE to be merged. Though there is little point to
5552 calling this function if TARGET_ABSPATH and all its subtrees have already
5553 had SOURCE merged, this will work but is a no-op.
5555 SOURCE->rev1 and SOURCE->rev2 must be bound by the set of remaining_ranges
5556 fields in CHILDREN_WITH_MERGEINFO's elements, specifically:
5558 For forward merges (SOURCE->rev1 < SOURCE->rev2):
5560 1) The first svn_merge_range_t * element of each child's remaining_ranges
5561 array must meet one of the following conditions:
5563 a) The range's start field is greater than or equal to SOURCE->rev2.
5565 b) The range's end field is SOURCE->rev2.
5567 2) Among all the ranges that meet condition 'b' the oldest start
5568 revision must equal SOURCE->rev1.
5570 For reverse merges (SOURCE->rev1 > SOURCE->rev2):
5572 1) The first svn_merge_range_t * element of each child's remaining_ranges
5573 array must meet one of the following conditions:
5575 a) The range's start field is less than or equal to SOURCE->rev2.
5577 b) The range's end field is SOURCE->rev2.
5579 2) Among all the ranges that meet condition 'b' the youngest start
5580 revision must equal SOURCE->rev1.
5582 Note: If the first svn_merge_range_t * element of some subtree child's
5583 remaining_ranges array is the same as the first range of that child's
5584 nearest path-wise ancestor, then the subtree child *will not* be described
5587 DEPTH, NOTIFY_B, and MERGE_B are cascaded from do_directory_merge(), see
5588 that function for more info.
5590 MERGE_B->ra_session1 and MERGE_B->ra_session2 are RA sessions open to any
5591 URL in the repository of SOURCE; they may be temporarily reparented within
5594 If SOURCE->ancestral is set, then SOURCE->loc1 must be a
5595 historical ancestor of SOURCE->loc2, or vice-versa (see
5596 `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements around
5597 SOURCE in this case).
5599 static svn_error_t *
5600 drive_merge_report_editor(const char *target_abspath,
5601 const merge_source_t *source,
5602 const apr_array_header_t *children_with_mergeinfo,
5603 const svn_diff_tree_processor_t *processor,
5605 merge_cmd_baton_t *merge_b,
5606 apr_pool_t *scratch_pool)
5608 const svn_ra_reporter3_t *reporter;
5609 const svn_delta_editor_t *diff_editor;
5610 void *diff_edit_baton;
5612 svn_revnum_t target_start;
5613 svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
5614 const char *old_sess1_url, *old_sess2_url;
5615 svn_boolean_t is_rollback = source->loc1->rev > source->loc2->rev;
5617 /* Start with a safe default starting revision for the editor and the
5619 target_start = source->loc1->rev;
5621 /* If we are honoring mergeinfo the starting revision for the merge target
5622 might not be SOURCE->rev1, in fact the merge target might not need *any*
5623 part of SOURCE merged -- Instead some subtree of the target
5624 needs SOURCE -- So get the right starting revision for the
5626 if (honor_mergeinfo)
5628 svn_client__merge_path_t *child;
5630 /* CHILDREN_WITH_MERGEINFO must always exist if we are honoring
5631 mergeinfo and must have at least one element (describing the
5633 SVN_ERR_ASSERT(children_with_mergeinfo);
5634 SVN_ERR_ASSERT(children_with_mergeinfo->nelts);
5636 /* Get the merge target's svn_client__merge_path_t, which is always
5637 the first in the array due to depth first sorting requirement,
5638 see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
5639 child = APR_ARRAY_IDX(children_with_mergeinfo, 0,
5640 svn_client__merge_path_t *);
5641 SVN_ERR_ASSERT(child);
5642 if (child->remaining_ranges->nelts == 0)
5644 /* The merge target doesn't need anything merged. */
5645 target_start = source->loc2->rev;
5649 /* The merge target has remaining revisions to merge. These
5650 ranges may fully or partially overlap the range described
5651 by SOURCE->rev1:rev2 or may not intersect that range at
5653 svn_merge_range_t *range =
5654 APR_ARRAY_IDX(child->remaining_ranges, 0,
5655 svn_merge_range_t *);
5656 if ((!is_rollback && range->start > source->loc2->rev)
5657 || (is_rollback && range->start < source->loc2->rev))
5659 /* Merge target's first remaining range doesn't intersect. */
5660 target_start = source->loc2->rev;
5664 /* Merge target's first remaining range partially or
5666 target_start = range->start;
5671 SVN_ERR(svn_client__ensure_ra_session_url(&old_sess1_url,
5672 merge_b->ra_session1,
5673 source->loc1->url, scratch_pool));
5674 /* Temporarily point our second RA session to SOURCE->loc1->url, too. We use
5675 this to request individual file contents. */
5676 SVN_ERR(svn_client__ensure_ra_session_url(&old_sess2_url,
5677 merge_b->ra_session2,
5678 source->loc1->url, scratch_pool));
5680 /* Get the diff editor and a reporter with which to, ultimately,
5682 SVN_ERR(svn_client__get_diff_editor2(&diff_editor, &diff_edit_baton,
5683 merge_b->ra_session2,
5686 TRUE /* text_deltas */,
5688 merge_b->ctx->cancel_func,
5689 merge_b->ctx->cancel_baton,
5691 SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1,
5692 &reporter, &report_baton, source->loc2->rev,
5693 "", depth, merge_b->diff_ignore_ancestry,
5694 TRUE, /* text_deltas */
5695 source->loc2->url, diff_editor, diff_edit_baton,
5698 /* Drive the reporter. */
5699 SVN_ERR(reporter->set_path(report_baton, "", target_start, depth,
5700 FALSE, NULL, scratch_pool));
5701 if (honor_mergeinfo && children_with_mergeinfo)
5703 /* Describe children with mergeinfo overlapping this merge
5704 operation such that no repeated diff is retrieved for them from
5707 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5709 /* Start with CHILDREN_WITH_MERGEINFO[1], CHILDREN_WITH_MERGEINFO[0]
5710 is always the merge target (TARGET_ABSPATH). */
5711 for (i = 1; i < children_with_mergeinfo->nelts; i++)
5713 svn_merge_range_t *range;
5714 const char *child_repos_path;
5715 const svn_client__merge_path_t *parent;
5716 const svn_client__merge_path_t *child =
5717 APR_ARRAY_IDX(children_with_mergeinfo, i,
5718 svn_client__merge_path_t *);
5720 SVN_ERR_ASSERT(child);
5724 svn_pool_clear(iterpool);
5726 /* Find this child's nearest wc ancestor with mergeinfo. */
5727 parent = find_nearest_ancestor(children_with_mergeinfo,
5728 FALSE, child->abspath);
5730 /* If a subtree needs the same range applied as its nearest parent
5731 with mergeinfo or neither the subtree nor this parent need
5732 SOURCE->rev1:rev2 merged, then we don't need to describe the
5733 subtree separately. In the latter case this could break the
5734 editor if child->abspath didn't exist at SOURCE->rev2 and we
5735 attempt to describe it via a reporter set_path call. */
5736 if (child->remaining_ranges->nelts)
5738 range = APR_ARRAY_IDX(child->remaining_ranges, 0,
5739 svn_merge_range_t *);
5740 if ((!is_rollback && range->start > source->loc2->rev)
5741 || (is_rollback && range->start < source->loc2->rev))
5743 /* This child's first remaining range comes after the range
5744 we are currently merging, so skip it. We expect to get
5745 to it in a subsequent call to this function. */
5748 else if (parent->remaining_ranges->nelts)
5750 svn_merge_range_t *parent_range =
5751 APR_ARRAY_IDX(parent->remaining_ranges, 0,
5752 svn_merge_range_t *);
5753 svn_merge_range_t *child_range =
5754 APR_ARRAY_IDX(child->remaining_ranges, 0,
5755 svn_merge_range_t *);
5756 if (parent_range->start == child_range->start)
5757 continue; /* Subtree needs same range as parent. */
5760 else /* child->remaining_ranges->nelts == 0*/
5762 /* If both the subtree and its parent need no ranges applied
5763 consider that as the "same ranges" and don't describe
5765 if (parent->remaining_ranges->nelts == 0)
5769 /* Ok, we really need to describe this subtree as it needs different
5770 ranges applied than its nearest working copy parent. */
5771 child_repos_path = svn_dirent_is_child(target_abspath,
5774 /* This loop is only processing subtrees, so CHILD->ABSPATH
5775 better be a proper child of the merge target. */
5776 SVN_ERR_ASSERT(child_repos_path);
5778 if ((child->remaining_ranges->nelts == 0)
5779 || (is_rollback && (range->start < source->loc2->rev))
5780 || (!is_rollback && (range->start > source->loc2->rev)))
5782 /* Nothing to merge to this child. We'll claim we have
5783 it up to date so the server doesn't send us
5785 SVN_ERR(reporter->set_path(report_baton, child_repos_path,
5786 source->loc2->rev, depth, FALSE,
5791 SVN_ERR(reporter->set_path(report_baton, child_repos_path,
5792 range->start, depth, FALSE,
5796 svn_pool_destroy(iterpool);
5798 SVN_ERR(reporter->finish_report(report_baton, scratch_pool));
5800 /* Point the merge baton's RA sessions back where they were. */
5801 SVN_ERR(svn_ra_reparent(merge_b->ra_session1, old_sess1_url, scratch_pool));
5802 SVN_ERR(svn_ra_reparent(merge_b->ra_session2, old_sess2_url, scratch_pool));
5804 return SVN_NO_ERROR;
5807 /* Iterate over each svn_client__merge_path_t * element in
5808 CHILDREN_WITH_MERGEINFO and, if START_REV is true, find the most inclusive
5809 start revision among those element's first remaining_ranges element. If
5810 START_REV is false, then look for the most inclusive end revision.
5812 If IS_ROLLBACK is true the youngest start or end (as per START_REV)
5813 revision is considered the "most inclusive" otherwise the oldest revision
5816 If none of CHILDREN_WITH_MERGEINFO's elements have any remaining ranges
5817 return SVN_INVALID_REVNUM. */
5819 get_most_inclusive_rev(const apr_array_header_t *children_with_mergeinfo,
5820 svn_boolean_t is_rollback,
5821 svn_boolean_t start_rev)
5824 svn_revnum_t most_inclusive_rev = SVN_INVALID_REVNUM;
5826 for (i = 0; i < children_with_mergeinfo->nelts; i++)
5828 svn_client__merge_path_t *child =
5829 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5831 if ((! child) || child->absent)
5833 if (child->remaining_ranges->nelts > 0)
5835 svn_merge_range_t *range =
5836 APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
5838 /* Are we looking for the most inclusive start or end rev? */
5839 svn_revnum_t rev = start_rev ? range->start : range->end;
5841 if ((most_inclusive_rev == SVN_INVALID_REVNUM)
5842 || (is_rollback && (rev > most_inclusive_rev))
5843 || ((! is_rollback) && (rev < most_inclusive_rev)))
5844 most_inclusive_rev = rev;
5847 return most_inclusive_rev;
5851 /* If first item in each child of CHILDREN_WITH_MERGEINFO's
5852 remaining_ranges is inclusive of END_REV, Slice the first range in
5853 to two at END_REV. All the allocations are persistent and allocated
5856 slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
5857 svn_boolean_t is_rollback, svn_revnum_t end_rev,
5861 for (i = 0; i < children_with_mergeinfo->nelts; i++)
5863 svn_client__merge_path_t *child =
5864 APR_ARRAY_IDX(children_with_mergeinfo, i,
5865 svn_client__merge_path_t *);
5866 if (!child || child->absent)
5868 if (child->remaining_ranges->nelts > 0)
5870 svn_merge_range_t *range = APR_ARRAY_IDX(child->remaining_ranges, 0,
5871 svn_merge_range_t *);
5872 if ((is_rollback && (range->start > end_rev)
5873 && (range->end < end_rev))
5874 || (!is_rollback && (range->start < end_rev)
5875 && (range->end > end_rev)))
5877 svn_merge_range_t *split_range1, *split_range2;
5879 split_range1 = svn_merge_range_dup(range, pool);
5880 split_range2 = svn_merge_range_dup(range, pool);
5881 split_range1->end = end_rev;
5882 split_range2->start = end_rev;
5883 APR_ARRAY_IDX(child->remaining_ranges, 0,
5884 svn_merge_range_t *) = split_range1;
5885 svn_sort__array_insert(&split_range2, child->remaining_ranges, 1);
5891 /* Helper for do_directory_merge().
5893 For each child in CHILDREN_WITH_MERGEINFO remove the first remaining_ranges
5894 svn_merge_range_t *element of the child if that range has an end revision
5897 If a range is removed from a child's remaining_ranges array, allocate the
5898 new remaining_ranges array in POOL.
5901 remove_first_range_from_remaining_ranges(svn_revnum_t revision,
5903 *children_with_mergeinfo,
5908 for (i = 0; i < children_with_mergeinfo->nelts; i++)
5910 svn_client__merge_path_t *child =
5911 APR_ARRAY_IDX(children_with_mergeinfo, i,
5912 svn_client__merge_path_t *);
5913 if (!child || child->absent)
5915 if (child->remaining_ranges->nelts > 0)
5917 svn_merge_range_t *first_range =
5918 APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
5919 if (first_range->end == revision)
5921 svn_sort__array_delete(child->remaining_ranges, 0, 1);
5927 /* Get a file's content and properties from the repository.
5928 Set *FILENAME to the local path to a new temporary file holding its text,
5929 and set *PROPS to a new hash of its properties.
5931 RA_SESSION is a session open to the correct repository, which will be
5932 temporarily reparented to the URL of the file itself. LOCATION is the
5933 repository location of the file.
5935 The resulting file and the return values live as long as RESULT_POOL, all
5936 other allocations occur in SCRATCH_POOL.
5938 static svn_error_t *
5939 single_file_merge_get_file(const char **filename,
5941 svn_ra_session_t *ra_session,
5942 const svn_client__pathrev_t *location,
5943 const char *wc_target,
5944 apr_pool_t *result_pool,
5945 apr_pool_t *scratch_pool)
5947 svn_stream_t *stream;
5948 const char *old_sess_url;
5951 SVN_ERR(svn_stream_open_unique(&stream, filename, NULL,
5952 svn_io_file_del_on_pool_cleanup,
5953 result_pool, scratch_pool));
5955 SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url, ra_session, location->url,
5957 err = svn_ra_get_file(ra_session, "", location->rev,
5958 stream, NULL, props, scratch_pool);
5959 SVN_ERR(svn_error_compose_create(
5960 err, svn_ra_reparent(ra_session, old_sess_url, scratch_pool)));
5962 return svn_error_trace(svn_stream_close(stream));
5965 /* Compare two svn_client__merge_path_t elements **A and **B, given the
5966 addresses of pointers to them. Return an integer less than, equal to, or
5967 greater than zero if A sorts before, the same as, or after B, respectively.
5968 This is a helper for qsort() and bsearch() on an array of such elements. */
5970 compare_merge_path_t_as_paths(const void *a,
5973 const svn_client__merge_path_t *child1
5974 = *((const svn_client__merge_path_t * const *) a);
5975 const svn_client__merge_path_t *child2
5976 = *((const svn_client__merge_path_t * const *) b);
5978 return svn_path_compare_paths(child1->abspath, child2->abspath);
5981 /* Return a pointer to the element of CHILDREN_WITH_MERGEINFO whose path
5982 * is PATH, or return NULL if there is no such element. */
5983 static svn_client__merge_path_t *
5984 get_child_with_mergeinfo(const apr_array_header_t *children_with_mergeinfo,
5985 const char *abspath)
5987 svn_client__merge_path_t merge_path;
5988 svn_client__merge_path_t *key;
5989 svn_client__merge_path_t **pchild;
5991 merge_path.abspath = abspath;
5993 pchild = bsearch(&key, children_with_mergeinfo->elts,
5994 children_with_mergeinfo->nelts,
5995 children_with_mergeinfo->elt_size,
5996 compare_merge_path_t_as_paths);
5997 return pchild ? *pchild : NULL;
6000 /* Insert a deep copy of INSERT_ELEMENT into the CHILDREN_WITH_MERGEINFO
6001 array at its correct position. Allocate the new storage in POOL.
6002 CHILDREN_WITH_MERGEINFO is a depth first sorted array of
6003 (svn_client__merge_path_t *).
6005 ### Most callers don't need this to deep-copy the new element.
6006 ### It may be more efficient for some callers to insert a bunch of items
6007 out of order and then sort afterwards. (One caller is doing a qsort
6008 after calling this anyway.)
6011 insert_child_to_merge(apr_array_header_t *children_with_mergeinfo,
6012 const svn_client__merge_path_t *insert_element,
6016 const svn_client__merge_path_t *new_element;
6018 /* Find where to insert the new element */
6020 svn_sort__bsearch_lower_bound(&insert_element, children_with_mergeinfo,
6021 compare_merge_path_t_as_paths);
6023 new_element = svn_client__merge_path_dup(insert_element, pool);
6024 svn_sort__array_insert(&new_element, children_with_mergeinfo, insert_index);
6027 /* Helper for get_mergeinfo_paths().
6029 CHILDREN_WITH_MERGEINFO, DEPTH, and POOL are
6030 all cascaded from the arguments of the same name to get_mergeinfo_paths().
6032 TARGET is the merge target.
6034 *CHILD is the element in in CHILDREN_WITH_MERGEINFO that
6035 get_mergeinfo_paths() is iterating over and *CURR_INDEX is index for
6038 If CHILD->ABSPATH is equal to MERGE_CMD_BATON->target->abspath do nothing.
6039 Else if CHILD->ABSPATH is switched or absent then make sure its immediate
6040 (as opposed to nearest) parent in CHILDREN_WITH_MERGEINFO is marked as
6041 missing a child. If the immediate parent does not exist in
6042 CHILDREN_WITH_MERGEINFO then create it (and increment *CURR_INDEX so that
6043 caller doesn't process the inserted element). Also ensure that
6044 CHILD->ABSPATH's siblings which are not already present in
6045 CHILDREN_WITH_MERGEINFO are also added to the array, limited by DEPTH
6046 (e.g. don't add directory siblings of a switched file).
6047 Use POOL for temporary allocations only, any new CHILDREN_WITH_MERGEINFO
6048 elements are allocated in POOL. */
6049 static svn_error_t *
6050 insert_parent_and_sibs_of_sw_absent_del_subtree(
6051 apr_array_header_t *children_with_mergeinfo,
6052 const merge_target_t *target,
6054 svn_client__merge_path_t *child,
6056 svn_client_ctx_t *ctx,
6059 svn_client__merge_path_t *parent;
6060 const char *parent_abspath;
6061 apr_pool_t *iterpool;
6062 const apr_array_header_t *children;
6067 && strcmp(target->abspath,
6068 child->abspath) != 0)))
6069 return SVN_NO_ERROR;
6071 parent_abspath = svn_dirent_dirname(child->abspath, pool);
6072 parent = get_child_with_mergeinfo(children_with_mergeinfo, parent_abspath);
6075 parent->missing_child = child->absent;
6076 parent->switched_child = child->switched;
6080 /* Create a new element to insert into CHILDREN_WITH_MERGEINFO. */
6081 parent = svn_client__merge_path_create(parent_abspath, pool);
6082 parent->missing_child = child->absent;
6083 parent->switched_child = child->switched;
6084 /* Insert PARENT into CHILDREN_WITH_MERGEINFO. */
6085 insert_child_to_merge(children_with_mergeinfo, parent, pool);
6086 /* Increment for loop index so we don't process the inserted element. */
6088 } /*(parent == NULL) */
6090 /* Add all of PARENT's non-missing children that are not already present.*/
6091 SVN_ERR(svn_wc__node_get_children(&children, ctx->wc_ctx,
6092 parent_abspath, FALSE, pool, pool));
6093 iterpool = svn_pool_create(pool);
6094 for (i = 0; i < children->nelts; i++)
6096 const char *child_abspath = APR_ARRAY_IDX(children, i, const char *);
6097 svn_client__merge_path_t *sibling_of_missing;
6099 svn_pool_clear(iterpool);
6101 /* Does this child already exist in CHILDREN_WITH_MERGEINFO? */
6102 sibling_of_missing = get_child_with_mergeinfo(children_with_mergeinfo,
6104 /* Create the missing child and insert it into CHILDREN_WITH_MERGEINFO.*/
6105 if (!sibling_of_missing)
6107 /* Don't add directory children if DEPTH is svn_depth_files. */
6108 if (depth == svn_depth_files)
6110 svn_node_kind_t child_kind;
6112 SVN_ERR(svn_wc_read_kind2(&child_kind,
6113 ctx->wc_ctx, child_abspath,
6114 FALSE, FALSE, iterpool));
6115 if (child_kind != svn_node_file)
6119 sibling_of_missing = svn_client__merge_path_create(child_abspath,
6121 insert_child_to_merge(children_with_mergeinfo, sibling_of_missing,
6126 svn_pool_destroy(iterpool);
6128 return SVN_NO_ERROR;
6131 /* pre_merge_status_cb's baton */
6132 struct pre_merge_status_baton_t
6134 svn_wc_context_t *wc_ctx;
6136 /* const char *absolute_wc_path to svn_depth_t * mapping for depths
6137 of empty, immediates, and files. */
6138 apr_hash_t *shallow_subtrees;
6140 /* const char *absolute_wc_path to the same, for all paths missing
6141 from the working copy. */
6142 apr_hash_t *missing_subtrees;
6144 /* const char *absolute_wc_path const char * repos relative path, describing
6145 the root of each switched subtree in the working copy and the repository
6146 relative path it is switched to. */
6147 apr_hash_t *switched_subtrees;
6149 /* A pool to allocate additions to the above hashes in. */
6153 /* A svn_wc_status_func4_t callback used by get_mergeinfo_paths to gather
6154 all switched, depth filtered and missing subtrees under a merge target.
6156 Note that this doesn't see server and user excluded trees. */
6157 static svn_error_t *
6158 pre_merge_status_cb(void *baton,
6159 const char *local_abspath,
6160 const svn_wc_status3_t *status,
6161 apr_pool_t *scratch_pool)
6163 struct pre_merge_status_baton_t *pmsb = baton;
6165 if (status->switched && !status->file_external)
6167 store_path(pmsb->switched_subtrees, local_abspath);
6170 if (status->depth == svn_depth_empty
6171 || status->depth == svn_depth_files)
6173 const char *dup_abspath;
6174 svn_depth_t *depth = apr_pmemdup(pmsb->pool, &status->depth,
6177 dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
6179 svn_hash_sets(pmsb->shallow_subtrees, dup_abspath, depth);
6182 if (status->node_status == svn_wc_status_missing)
6184 svn_boolean_t new_missing_root = TRUE;
6185 apr_hash_index_t *hi;
6187 for (hi = apr_hash_first(scratch_pool, pmsb->missing_subtrees);
6189 hi = apr_hash_next(hi))
6191 const char *missing_root_path = svn__apr_hash_index_key(hi);
6193 if (svn_dirent_is_ancestor(missing_root_path,
6196 new_missing_root = FALSE;
6201 if (new_missing_root)
6202 store_path(pmsb->missing_subtrees, local_abspath);
6205 return SVN_NO_ERROR;
6208 /* Find all the subtrees in the working copy tree rooted at TARGET_ABSPATH
6209 * that have explicit mergeinfo.
6210 * Set *SUBTREES_WITH_MERGEINFO to a hash mapping (const char *) absolute
6211 * WC path to (svn_mergeinfo_t *) mergeinfo.
6213 * ### Is this function equivalent to:
6215 * svn_client__get_wc_mergeinfo_catalog(
6216 * subtrees_with_mergeinfo, inherited=NULL, include_descendants=TRUE,
6217 * svn_mergeinfo_explicit, target_abspath, limit_path=NULL,
6218 * walked_path=NULL, ignore_invalid_mergeinfo=FALSE, ...)
6220 * except for the catalog keys being abspaths instead of repo-relpaths?
6222 static svn_error_t *
6223 get_wc_explicit_mergeinfo_catalog(apr_hash_t **subtrees_with_mergeinfo,
6224 const char *target_abspath,
6226 svn_client_ctx_t *ctx,
6227 apr_pool_t *result_pool,
6228 apr_pool_t *scratch_pool)
6230 svn_opt_revision_t working_revision = { svn_opt_revision_working, { 0 } };
6231 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6232 apr_hash_index_t *hi;
6233 apr_hash_t *externals;
6235 SVN_ERR(svn_client_propget5(subtrees_with_mergeinfo, NULL,
6236 SVN_PROP_MERGEINFO, target_abspath,
6237 &working_revision, &working_revision, NULL,
6238 depth, NULL, ctx, result_pool, scratch_pool));
6240 SVN_ERR(svn_wc__externals_defined_below(&externals, ctx->wc_ctx,
6241 target_abspath, scratch_pool,
6244 /* Convert property values to svn_mergeinfo_t. */
6245 for (hi = apr_hash_first(scratch_pool, *subtrees_with_mergeinfo);
6247 hi = apr_hash_next(hi))
6249 const char *wc_path = svn__apr_hash_index_key(hi);
6250 svn_string_t *mergeinfo_string = svn__apr_hash_index_val(hi);
6251 svn_mergeinfo_t mergeinfo;
6254 /* svn_client_propget5 picks up file externals with
6255 mergeinfo, but we don't want those. */
6256 if (svn_hash_gets(externals, wc_path))
6258 svn_hash_sets(*subtrees_with_mergeinfo, wc_path, NULL);
6262 svn_pool_clear(iterpool);
6264 err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_string->data,
6268 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
6270 err = svn_error_createf(
6271 SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
6272 _("Invalid mergeinfo detected on '%s', "
6273 "merge tracking not possible"),
6274 svn_dirent_local_style(wc_path, scratch_pool));
6276 return svn_error_trace(err);
6278 svn_hash_sets(*subtrees_with_mergeinfo, wc_path, mergeinfo);
6280 svn_pool_destroy(iterpool);
6282 return SVN_NO_ERROR;
6285 /* Helper for do_directory_merge() when performing merge-tracking aware
6288 Walk of the working copy tree rooted at TARGET->abspath to
6289 depth DEPTH. Create an svn_client__merge_path_t * for any path which meets
6290 one or more of the following criteria:
6292 1) Path has working svn:mergeinfo.
6293 2) Path is switched.
6294 3) Path is a subtree of the merge target (i.e. is not equal to
6295 TARGET->abspath) and has no mergeinfo of its own but
6296 its immediate parent has mergeinfo with non-inheritable ranges. If
6297 this isn't a dry-run and the merge is between differences in the same
6298 repository, then this function will set working mergeinfo on the path
6299 equal to the mergeinfo inheritable from its parent.
6300 4) Path has an immediate child (or children) missing from the WC because
6301 the child is switched or absent from the WC, or due to a sparse
6303 5) Path has a sibling (or siblings) missing from the WC because the
6304 sibling is switched, absent, scheduled for deletion, or missing due to
6306 6) Path is absent from disk due to an authz restriction.
6307 7) Path is equal to TARGET->abspath.
6308 8) Path is an immediate *directory* child of
6309 TARGET->abspath and DEPTH is svn_depth_immediates.
6310 9) Path is an immediate *file* child of TARGET->abspath
6311 and DEPTH is svn_depth_files.
6312 10) Path is at a depth of 'empty' or 'files'.
6313 11) Path is missing from disk (e.g. due to an OS-level deletion).
6315 If subtrees within the requested DEPTH are unexpectedly missing disk,
6316 then raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE.
6318 Store the svn_client__merge_path_t *'s in *CHILDREN_WITH_MERGEINFO in
6319 depth-first order based on the svn_client__merge_path_t *s path member as
6320 sorted by svn_path_compare_paths(). Set the remaining_ranges field of each
6323 Note: Since the walk is rooted at TARGET->abspath, the
6324 latter is guaranteed to be in *CHILDREN_WITH_MERGEINFO and due to the
6325 depth-first ordering it is guaranteed to be the first element in
6326 *CHILDREN_WITH_MERGEINFO.
6328 MERGE_CMD_BATON is cascaded from the argument of the same name in
6329 do_directory_merge().
6331 static svn_error_t *
6332 get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
6333 const merge_target_t *target,
6335 svn_boolean_t dry_run,
6336 svn_boolean_t same_repos,
6337 svn_client_ctx_t *ctx,
6338 apr_pool_t *result_pool,
6339 apr_pool_t *scratch_pool)
6342 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6343 apr_hash_t *subtrees_with_mergeinfo;
6344 apr_hash_t *excluded_subtrees;
6345 apr_hash_t *switched_subtrees;
6346 apr_hash_t *shallow_subtrees;
6347 apr_hash_t *missing_subtrees;
6348 struct pre_merge_status_baton_t pre_merge_status_baton;
6350 /* Case 1: Subtrees with explicit mergeinfo. */
6351 SVN_ERR(get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
6354 result_pool, scratch_pool));
6355 if (subtrees_with_mergeinfo)
6357 apr_hash_index_t *hi;
6359 for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
6361 hi = apr_hash_next(hi))
6363 const char *wc_path = svn__apr_hash_index_key(hi);
6364 svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi);
6365 svn_client__merge_path_t *mergeinfo_child =
6366 svn_client__merge_path_create(wc_path, result_pool);
6368 svn_pool_clear(iterpool);
6370 /* Stash this child's pre-existing mergeinfo. */
6371 mergeinfo_child->pre_merge_mergeinfo = mergeinfo;
6373 /* Note if this child has non-inheritable mergeinfo */
6374 mergeinfo_child->has_noninheritable
6375 = svn_mergeinfo__is_noninheritable(
6376 mergeinfo_child->pre_merge_mergeinfo, iterpool);
6378 /* Append it. We'll sort below. */
6379 APR_ARRAY_PUSH(children_with_mergeinfo, svn_client__merge_path_t *)
6380 = svn_client__merge_path_dup(mergeinfo_child, result_pool);
6383 /* Sort CHILDREN_WITH_MERGEINFO by each child's path (i.e. as per
6384 compare_merge_path_t_as_paths). Any subsequent insertions of new
6385 children with insert_child_to_merge() require this ordering. */
6386 qsort(children_with_mergeinfo->elts,
6387 children_with_mergeinfo->nelts,
6388 children_with_mergeinfo->elt_size,
6389 compare_merge_path_t_as_paths);
6392 /* Case 2: Switched subtrees
6393 Case 10: Paths at depths of 'empty' or 'files'
6394 Case 11: Paths missing from disk */
6395 pre_merge_status_baton.wc_ctx = ctx->wc_ctx;
6396 switched_subtrees = apr_hash_make(scratch_pool);
6397 pre_merge_status_baton.switched_subtrees = switched_subtrees;
6398 shallow_subtrees = apr_hash_make(scratch_pool);
6399 pre_merge_status_baton.shallow_subtrees = shallow_subtrees;
6400 missing_subtrees = apr_hash_make(scratch_pool);
6401 pre_merge_status_baton.missing_subtrees = missing_subtrees;
6402 pre_merge_status_baton.pool = scratch_pool;
6403 SVN_ERR(svn_wc_walk_status(ctx->wc_ctx,
6407 FALSE /* no_ignore */,
6408 TRUE /* ignore_text_mods */,
6409 NULL /* ingore_patterns */,
6410 pre_merge_status_cb, &pre_merge_status_baton,
6411 ctx->cancel_func, ctx->cancel_baton,
6414 /* Issue #2915: Raise an error describing the roots of any missing
6415 subtrees, i.e. those that the WC thinks are on disk but have been
6416 removed outside of Subversion. */
6417 if (apr_hash_count(missing_subtrees))
6419 apr_hash_index_t *hi;
6420 svn_stringbuf_t *missing_subtree_err_buf =
6421 svn_stringbuf_create(_("Merge tracking not allowed with missing "
6422 "subtrees; try restoring these items "
6423 "first:\n"), scratch_pool);
6425 for (hi = apr_hash_first(scratch_pool, missing_subtrees);
6427 hi = apr_hash_next(hi))
6429 svn_pool_clear(iterpool);
6430 svn_stringbuf_appendcstr(missing_subtree_err_buf,
6431 svn_dirent_local_style(
6432 svn__apr_hash_index_key(hi), iterpool));
6433 svn_stringbuf_appendcstr(missing_subtree_err_buf, "\n");
6436 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
6437 NULL, missing_subtree_err_buf->data);
6440 if (apr_hash_count(switched_subtrees))
6442 apr_hash_index_t *hi;
6444 for (hi = apr_hash_first(scratch_pool, switched_subtrees);
6446 hi = apr_hash_next(hi))
6448 const char *wc_path = svn__apr_hash_index_key(hi);
6449 svn_client__merge_path_t *child = get_child_with_mergeinfo(
6450 children_with_mergeinfo, wc_path);
6454 child->switched = TRUE;
6458 svn_client__merge_path_t *switched_child =
6459 svn_client__merge_path_create(wc_path, result_pool);
6460 switched_child->switched = TRUE;
6461 insert_child_to_merge(children_with_mergeinfo, switched_child,
6467 if (apr_hash_count(shallow_subtrees))
6469 apr_hash_index_t *hi;
6471 for (hi = apr_hash_first(scratch_pool, shallow_subtrees);
6473 hi = apr_hash_next(hi))
6475 svn_boolean_t new_shallow_child = FALSE;
6476 const char *wc_path = svn__apr_hash_index_key(hi);
6477 svn_depth_t *child_depth = svn__apr_hash_index_val(hi);
6478 svn_client__merge_path_t *shallow_child = get_child_with_mergeinfo(
6479 children_with_mergeinfo, wc_path);
6483 if (*child_depth == svn_depth_empty
6484 || *child_depth == svn_depth_files)
6485 shallow_child->missing_child = TRUE;
6489 shallow_child = svn_client__merge_path_create(wc_path,
6491 new_shallow_child = TRUE;
6493 if (*child_depth == svn_depth_empty
6494 || *child_depth == svn_depth_files)
6495 shallow_child->missing_child = TRUE;
6498 /* A little trickery: If PATH doesn't have any mergeinfo or has
6499 only inheritable mergeinfo, we still describe it as having
6500 non-inheritable mergeinfo if it is missing a child due to
6501 a shallow depth. Why? Because the mergeinfo we'll add to PATH
6502 to describe the merge must be non-inheritable, so PATH's missing
6503 children don't inherit it. Marking these PATHs as non-
6504 inheritable allows the logic for case 3 to properly account
6505 for PATH's children. */
6506 if (!shallow_child->has_noninheritable
6507 && (*child_depth == svn_depth_empty
6508 || *child_depth == svn_depth_files))
6510 shallow_child->has_noninheritable = TRUE;
6513 if (new_shallow_child)
6514 insert_child_to_merge(children_with_mergeinfo, shallow_child,
6519 /* Case 6: Paths absent from disk due to server or user exclusion. */
6520 SVN_ERR(svn_wc__get_excluded_subtrees(&excluded_subtrees,
6521 ctx->wc_ctx, target->abspath,
6522 result_pool, scratch_pool));
6523 if (excluded_subtrees)
6525 apr_hash_index_t *hi;
6527 for (hi = apr_hash_first(scratch_pool, excluded_subtrees);
6529 hi = apr_hash_next(hi))
6531 const char *wc_path = svn__apr_hash_index_key(hi);
6532 svn_client__merge_path_t *child = get_child_with_mergeinfo(
6533 children_with_mergeinfo, wc_path);
6537 child->absent = TRUE;
6541 svn_client__merge_path_t *absent_child =
6542 svn_client__merge_path_create(wc_path, result_pool);
6543 absent_child->absent = TRUE;
6544 insert_child_to_merge(children_with_mergeinfo, absent_child,
6550 /* Case 7: The merge target MERGE_CMD_BATON->target->abspath is always
6552 if (!get_child_with_mergeinfo(children_with_mergeinfo,
6555 svn_client__merge_path_t *target_child =
6556 svn_client__merge_path_create(target->abspath,
6558 insert_child_to_merge(children_with_mergeinfo, target_child,
6562 /* Case 8: Path is an immediate *directory* child of
6563 MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_immediates.
6565 Case 9: Path is an immediate *file* child of
6566 MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_files. */
6567 if (depth == svn_depth_immediates || depth == svn_depth_files)
6570 const apr_array_header_t *immediate_children;
6572 SVN_ERR(svn_wc__node_get_children_of_working_node(
6573 &immediate_children, ctx->wc_ctx,
6574 target->abspath, FALSE, scratch_pool, scratch_pool));
6576 for (j = 0; j < immediate_children->nelts; j++)
6578 const char *immediate_child_abspath =
6579 APR_ARRAY_IDX(immediate_children, j, const char *);
6580 svn_node_kind_t immediate_child_kind;
6582 svn_pool_clear(iterpool);
6583 SVN_ERR(svn_wc_read_kind2(&immediate_child_kind,
6584 ctx->wc_ctx, immediate_child_abspath,
6585 FALSE, FALSE, iterpool));
6586 if ((immediate_child_kind == svn_node_dir
6587 && depth == svn_depth_immediates)
6588 || (immediate_child_kind == svn_node_file
6589 && depth == svn_depth_files))
6591 if (!get_child_with_mergeinfo(children_with_mergeinfo,
6592 immediate_child_abspath))
6594 svn_client__merge_path_t *immediate_child =
6595 svn_client__merge_path_create(immediate_child_abspath,
6598 if (immediate_child_kind == svn_node_dir
6599 && depth == svn_depth_immediates)
6600 immediate_child->immediate_child_dir = TRUE;
6602 insert_child_to_merge(children_with_mergeinfo,
6603 immediate_child, result_pool);
6609 /* If DEPTH isn't empty then cover cases 3), 4), and 5), possibly adding
6610 elements to CHILDREN_WITH_MERGEINFO. */
6611 if (depth <= svn_depth_empty)
6612 return SVN_NO_ERROR;
6614 for (i = 0; i < children_with_mergeinfo->nelts; i++)
6616 svn_client__merge_path_t *child =
6617 APR_ARRAY_IDX(children_with_mergeinfo, i,
6618 svn_client__merge_path_t *);
6619 svn_pool_clear(iterpool);
6621 /* Case 3) Where merging to a path with a switched child the path
6622 gets non-inheritable mergeinfo for the merge range performed and
6623 the child gets its own set of mergeinfo. If the switched child
6624 later "returns", e.g. a switched path is unswitched, the child
6625 may not have any explicit mergeinfo. If the initial merge is
6626 repeated we don't want to repeat the merge for the path, but we
6627 do want to repeat it for the previously switched child. To
6628 ensure this we check if all of CHILD's non-missing children have
6629 explicit mergeinfo (they should already be present in
6630 CHILDREN_WITH_MERGEINFO if they do). If not,
6631 add the children without mergeinfo to CHILDREN_WITH_MERGEINFO so
6632 do_directory_merge() will merge them independently.
6634 But that's not enough! Since do_directory_merge() performs
6635 the merges on the paths in CHILDREN_WITH_MERGEINFO in a depth
6636 first manner it will merge the previously switched path's parent
6637 first. As part of this merge it will update the parent's
6638 previously non-inheritable mergeinfo and make it inheritable
6639 (since it notices the path has no missing children), then when
6640 do_directory_merge() finally merges the previously missing
6641 child it needs to get mergeinfo from the child's nearest
6642 ancestor, but since do_directory_merge() already tweaked that
6643 mergeinfo, removing the non-inheritable flag, it appears that the
6644 child already has been merged to. To prevent this we set
6645 override mergeinfo on the child now, before any merging is done,
6646 so it has explicit mergeinfo that reflects only CHILD's
6647 inheritable mergeinfo. */
6649 /* If depth is immediates or files then don't add new children if
6650 CHILD is a subtree of the merge target; those children are below
6651 the operational depth of the merge. */
6652 if (child->has_noninheritable
6653 && (i == 0 || depth == svn_depth_infinity))
6655 const apr_array_header_t *children;
6658 SVN_ERR(svn_wc__node_get_children(&children,
6660 child->abspath, FALSE,
6661 iterpool, iterpool));
6662 for (j = 0; j < children->nelts; j++)
6664 svn_client__merge_path_t *child_of_noninheritable;
6665 const char *child_abspath = APR_ARRAY_IDX(children, j,
6668 /* Does this child already exist in CHILDREN_WITH_MERGEINFO?
6669 If not, create it and insert it into
6670 CHILDREN_WITH_MERGEINFO and set override mergeinfo on
6672 child_of_noninheritable =
6673 get_child_with_mergeinfo(children_with_mergeinfo,
6675 if (!child_of_noninheritable)
6677 /* Don't add directory children if DEPTH
6678 is svn_depth_files. */
6679 if (depth == svn_depth_files)
6681 svn_node_kind_t child_kind;
6682 SVN_ERR(svn_wc_read_kind2(&child_kind,
6683 ctx->wc_ctx, child_abspath,
6684 FALSE, FALSE, iterpool));
6685 if (child_kind != svn_node_file)
6688 /* else DEPTH is infinity or immediates so we want both
6689 directory and file children. */
6691 child_of_noninheritable =
6692 svn_client__merge_path_create(child_abspath, result_pool);
6693 child_of_noninheritable->child_of_noninheritable = TRUE;
6694 insert_child_to_merge(children_with_mergeinfo,
6695 child_of_noninheritable,
6697 if (!dry_run && same_repos)
6699 svn_mergeinfo_t mergeinfo;
6701 SVN_ERR(svn_client__get_wc_mergeinfo(
6703 svn_mergeinfo_nearest_ancestor,
6704 child_of_noninheritable->abspath,
6705 target->abspath, NULL, FALSE,
6706 ctx, iterpool, iterpool));
6708 SVN_ERR(svn_client__record_wc_mergeinfo(
6709 child_of_noninheritable->abspath, mergeinfo,
6710 FALSE, ctx, iterpool));
6715 /* Case 4 and 5 are handled by the following function. */
6716 SVN_ERR(insert_parent_and_sibs_of_sw_absent_del_subtree(
6717 children_with_mergeinfo, target, &i, child,
6718 depth, ctx, result_pool));
6719 } /* i < children_with_mergeinfo->nelts */
6720 svn_pool_destroy(iterpool);
6722 return SVN_NO_ERROR;
6726 /* Implements the svn_log_entry_receiver_t interface.
6728 * BATON is an 'apr_array_header_t *' array of 'svn_revnum_t'.
6729 * Push a copy of LOG_ENTRY->revision onto BATON. Thus, a
6730 * series of invocations of this callback accumulates the
6731 * corresponding set of revisions into BATON.
6733 static svn_error_t *
6734 log_changed_revs(void *baton,
6735 svn_log_entry_t *log_entry,
6738 apr_array_header_t *revs = baton;
6740 APR_ARRAY_PUSH(revs, svn_revnum_t) = log_entry->revision;
6741 return SVN_NO_ERROR;
6745 /* Set *MIN_REV_P to the oldest and *MAX_REV_P to the youngest start or end
6746 * revision occurring in RANGELIST, or to SVN_INVALID_REVNUM if RANGELIST
6749 merge_range_find_extremes(svn_revnum_t *min_rev_p,
6750 svn_revnum_t *max_rev_p,
6751 const svn_rangelist_t *rangelist)
6755 *min_rev_p = SVN_INVALID_REVNUM;
6756 *max_rev_p = SVN_INVALID_REVNUM;
6757 for (i = 0; i < rangelist->nelts; i++)
6759 svn_merge_range_t *range
6760 = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
6761 svn_revnum_t range_min = MIN(range->start, range->end);
6762 svn_revnum_t range_max = MAX(range->start, range->end);
6764 if ((! SVN_IS_VALID_REVNUM(*min_rev_p)) || (range_min < *min_rev_p))
6765 *min_rev_p = range_min;
6766 if ((! SVN_IS_VALID_REVNUM(*max_rev_p)) || (range_max > *max_rev_p))
6767 *max_rev_p = range_max;
6771 /* Wrapper around svn_ra_get_log2(). Invoke RECEIVER with RECEIVER_BATON
6772 * on each commit from YOUNGEST_REV to OLDEST_REV in which TARGET_RELPATH
6773 * changed. TARGET_RELPATH is relative to RA_SESSION's URL.
6774 * Important: Revision properties are not retrieved by this function for
6775 * performance reasons.
6777 static svn_error_t *
6778 get_log(svn_ra_session_t *ra_session,
6779 const char *target_relpath,
6780 svn_revnum_t youngest_rev,
6781 svn_revnum_t oldest_rev,
6782 svn_boolean_t discover_changed_paths,
6783 svn_log_entry_receiver_t receiver,
6784 void *receiver_baton,
6787 apr_array_header_t *log_targets;
6788 apr_array_header_t *revprops;
6790 log_targets = apr_array_make(pool, 1, sizeof(const char *));
6791 APR_ARRAY_PUSH(log_targets, const char *) = target_relpath;
6793 revprops = apr_array_make(pool, 0, sizeof(const char *));
6795 SVN_ERR(svn_ra_get_log2(ra_session, log_targets, youngest_rev,
6796 oldest_rev, 0 /* limit */, discover_changed_paths,
6797 FALSE /* strict_node_history */,
6798 FALSE /* include_merged_revisions */,
6799 revprops, receiver, receiver_baton, pool));
6801 return SVN_NO_ERROR;
6804 /* Set *OPERATIVE_RANGES_P to an array of svn_merge_range_t * merge
6805 range objects copied wholesale from RANGES which have the property
6806 that in some revision within that range the object identified by
6807 RA_SESSION was modified (if by "modified" we mean "'svn log' would
6808 return that revision). *OPERATIVE_RANGES_P is allocated from the
6809 same pool as RANGES, and the ranges within it are shared with
6812 *OPERATIVE_RANGES_P may be the same as RANGES (that is, the output
6813 parameter is set only after the input is no longer used).
6815 Use POOL for temporary allocations. */
6816 static svn_error_t *
6817 remove_noop_merge_ranges(svn_rangelist_t **operative_ranges_p,
6818 svn_ra_session_t *ra_session,
6819 const svn_rangelist_t *ranges,
6823 svn_revnum_t oldest_rev, youngest_rev;
6824 apr_array_header_t *changed_revs =
6825 apr_array_make(pool, ranges->nelts, sizeof(svn_revnum_t));
6826 svn_rangelist_t *operative_ranges =
6827 apr_array_make(ranges->pool, ranges->nelts, ranges->elt_size);
6829 /* Find the revision extremes of the RANGES we have. */
6830 merge_range_find_extremes(&oldest_rev, &youngest_rev, ranges);
6831 if (SVN_IS_VALID_REVNUM(oldest_rev))
6832 oldest_rev++; /* make it inclusive */
6834 /* Get logs across those ranges, recording which revisions hold
6835 changes to our object's history. */
6836 SVN_ERR(get_log(ra_session, "", youngest_rev, oldest_rev, FALSE,
6837 log_changed_revs, changed_revs, pool));
6839 /* Are there *any* changes? */
6840 if (changed_revs->nelts)
6842 /* Our list of changed revisions should be in youngest-to-oldest
6844 svn_revnum_t youngest_changed_rev
6845 = APR_ARRAY_IDX(changed_revs, 0, svn_revnum_t);
6846 svn_revnum_t oldest_changed_rev
6847 = APR_ARRAY_IDX(changed_revs, changed_revs->nelts - 1, svn_revnum_t);
6849 /* Now, copy from RANGES to *OPERATIVE_RANGES, filtering out ranges
6850 that aren't operative (by virtue of not having any revisions
6851 represented in the CHANGED_REVS array). */
6852 for (i = 0; i < ranges->nelts; i++)
6854 svn_merge_range_t *range = APR_ARRAY_IDX(ranges, i,
6855 svn_merge_range_t *);
6856 svn_revnum_t range_min = MIN(range->start, range->end) + 1;
6857 svn_revnum_t range_max = MAX(range->start, range->end);
6860 /* If the merge range is entirely outside the range of changed
6861 revisions, we've no use for it. */
6862 if ((range_min > youngest_changed_rev)
6863 || (range_max < oldest_changed_rev))
6866 /* Walk through the changed_revs to see if any of them fall
6867 inside our current range. */
6868 for (j = 0; j < changed_revs->nelts; j++)
6870 svn_revnum_t changed_rev
6871 = APR_ARRAY_IDX(changed_revs, j, svn_revnum_t);
6872 if ((changed_rev >= range_min) && (changed_rev <= range_max))
6874 APR_ARRAY_PUSH(operative_ranges, svn_merge_range_t *) =
6882 *operative_ranges_p = operative_ranges;
6883 return SVN_NO_ERROR;
6887 /*-----------------------------------------------------------------------*/
6889 /*** Merge Source Normalization ***/
6891 /* qsort-compatible sort routine, rating merge_source_t * objects to
6892 be in descending (youngest-to-oldest) order based on their ->loc1->rev
6895 compare_merge_source_ts(const void *a,
6898 svn_revnum_t a_rev = (*(const merge_source_t *const *)a)->loc1->rev;
6899 svn_revnum_t b_rev = (*(const merge_source_t *const *)b)->loc1->rev;
6902 return a_rev < b_rev ? 1 : -1;
6905 /* Set *MERGE_SOURCE_TS_P to a list of merge sources generated by
6906 slicing history location SEGMENTS with a given requested merge
6907 RANGE. Use SOURCE_LOC for full source URL calculation.
6909 Order the merge sources in *MERGE_SOURCE_TS_P from oldest to
6911 static svn_error_t *
6912 combine_range_with_segments(apr_array_header_t **merge_source_ts_p,
6913 const svn_merge_range_t *range,
6914 const apr_array_header_t *segments,
6915 const svn_client__pathrev_t *source_loc,
6918 apr_array_header_t *merge_source_ts =
6919 apr_array_make(pool, 1, sizeof(merge_source_t *));
6920 svn_revnum_t minrev = MIN(range->start, range->end) + 1;
6921 svn_revnum_t maxrev = MAX(range->start, range->end);
6922 svn_boolean_t subtractive = (range->start > range->end);
6925 for (i = 0; i < segments->nelts; i++)
6927 svn_location_segment_t *segment =
6928 APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
6929 svn_client__pathrev_t *loc1, *loc2;
6930 merge_source_t *merge_source;
6931 const char *path1 = NULL;
6934 /* If this segment doesn't overlap our range at all, or
6935 represents a gap, ignore it. */
6936 if ((segment->range_end < minrev)
6937 || (segment->range_start > maxrev)
6938 || (! segment->path))
6941 /* If our range spans a segment boundary, we have to point our
6942 merge_source_t's path1 to the path of the immediately older
6943 segment, else it points to the same location as its path2. */
6944 rev1 = MAX(segment->range_start, minrev) - 1;
6945 if (minrev <= segment->range_start)
6949 path1 = (APR_ARRAY_IDX(segments, i - 1,
6950 svn_location_segment_t *))->path;
6952 /* If we've backed PATH1 up into a segment gap, let's back
6953 it up further still to the segment before the gap. We'll
6954 have to adjust rev1, too. */
6955 if ((! path1) && (i > 1))
6957 path1 = (APR_ARRAY_IDX(segments, i - 2,
6958 svn_location_segment_t *))->path;
6959 rev1 = (APR_ARRAY_IDX(segments, i - 2,
6960 svn_location_segment_t *))->range_end;
6965 path1 = apr_pstrdup(pool, segment->path);
6968 /* If we don't have two valid paths, we won't know what to do
6969 when merging. This could happen if someone requested a merge
6970 where the source didn't exist in a particular revision or
6971 something. The merge code would probably bomb out anyway, so
6972 we'll just *not* create a merge source in this case. */
6973 if (! (path1 && segment->path))
6976 /* Build our merge source structure. */
6977 loc1 = svn_client__pathrev_create_with_relpath(
6978 source_loc->repos_root_url, source_loc->repos_uuid,
6980 loc2 = svn_client__pathrev_create_with_relpath(
6981 source_loc->repos_root_url, source_loc->repos_uuid,
6982 MIN(segment->range_end, maxrev), segment->path, pool);
6983 /* If this is subtractive, reverse the whole calculation. */
6985 merge_source = merge_source_create(loc2, loc1, TRUE /* ancestral */,
6988 merge_source = merge_source_create(loc1, loc2, TRUE /* ancestral */,
6991 APR_ARRAY_PUSH(merge_source_ts, merge_source_t *) = merge_source;
6994 /* If this was a subtractive merge, and we created more than one
6995 merge source, we need to reverse the sort ordering of our sources. */
6996 if (subtractive && (merge_source_ts->nelts > 1))
6997 qsort(merge_source_ts->elts, merge_source_ts->nelts,
6998 merge_source_ts->elt_size, compare_merge_source_ts);
7000 *merge_source_ts_p = merge_source_ts;
7001 return SVN_NO_ERROR;
7004 /* Similar to normalize_merge_sources() except the input MERGE_RANGE_TS is a
7007 static svn_error_t *
7008 normalize_merge_sources_internal(apr_array_header_t **merge_sources_p,
7009 const svn_client__pathrev_t *source_loc,
7010 const svn_rangelist_t *merge_range_ts,
7011 svn_ra_session_t *ra_session,
7012 svn_client_ctx_t *ctx,
7013 apr_pool_t *result_pool,
7014 apr_pool_t *scratch_pool)
7016 svn_revnum_t source_peg_revnum = source_loc->rev;
7017 svn_revnum_t oldest_requested, youngest_requested;
7018 svn_revnum_t trim_revision = SVN_INVALID_REVNUM;
7019 apr_array_header_t *segments;
7022 /* Initialize our return variable. */
7023 *merge_sources_p = apr_array_make(result_pool, 1, sizeof(merge_source_t *));
7025 /* No ranges to merge? No problem. */
7026 if (merge_range_ts->nelts == 0)
7027 return SVN_NO_ERROR;
7029 /* Find the extremes of the revisions across our set of ranges. */
7030 merge_range_find_extremes(&oldest_requested, &youngest_requested,
7033 /* ### FIXME: Our underlying APIs can't yet handle the case where
7034 the peg revision isn't the youngest of the three revisions. So
7035 we'll just verify that the source in the peg revision is related
7036 to the source in the youngest requested revision (which is
7037 all the underlying APIs would do in this case right now anyway). */
7038 if (source_peg_revnum < youngest_requested)
7040 svn_client__pathrev_t *start_loc;
7042 SVN_ERR(svn_client__repos_location(&start_loc,
7043 ra_session, source_loc,
7045 ctx, scratch_pool, scratch_pool));
7046 source_peg_revnum = youngest_requested;
7049 /* Fetch the locations for our merge range span. */
7050 SVN_ERR(svn_client__repos_location_segments(&segments,
7051 ra_session, source_loc->url,
7057 /* See if we fetched enough history to do the job. "Surely we did,"
7058 you say. "After all, we covered the entire requested merge
7059 range." Yes, that's true, but if our first segment doesn't
7060 extend back to the oldest request revision, we've got a special
7061 case to deal with. Or if the first segment represents a gap,
7062 that's another special case. */
7063 trim_revision = SVN_INVALID_REVNUM;
7064 if (segments->nelts)
7066 svn_location_segment_t *first_segment =
7067 APR_ARRAY_IDX(segments, 0, svn_location_segment_t *);
7069 /* If the first segment doesn't start with the OLDEST_REQUESTED
7070 revision, we'll need to pass a trim revision to our range
7072 if (first_segment->range_start != oldest_requested)
7074 trim_revision = first_segment->range_start;
7077 /* Else, if the first segment has no path (and therefore is a
7078 gap), then we'll fetch the copy source revision from the
7079 second segment (provided there is one, of course) and use it
7080 to prepend an extra pathful segment to our list.
7082 ### We could avoid this bit entirely if we'd passed
7083 ### SVN_INVALID_REVNUM instead of OLDEST_REQUESTED to
7084 ### svn_client__repos_location_segments(), but that would
7085 ### really penalize clients hitting pre-1.5 repositories with
7086 ### the typical small merge range request (because of the
7087 ### lack of a node-origins cache in the repository). */
7088 else if (! first_segment->path)
7090 if (segments->nelts > 1)
7092 svn_location_segment_t *second_segment =
7093 APR_ARRAY_IDX(segments, 1, svn_location_segment_t *);
7094 const char *segment_url;
7095 const char *original_repos_relpath;
7096 svn_revnum_t original_revision;
7097 svn_opt_revision_t range_start_rev;
7098 range_start_rev.kind = svn_opt_revision_number;
7099 range_start_rev.value.number = second_segment->range_start;
7101 segment_url = svn_path_url_add_component2(
7102 source_loc->repos_root_url, second_segment->path,
7104 SVN_ERR(svn_client__get_copy_source(&original_repos_relpath,
7107 &range_start_rev, ctx,
7108 result_pool, scratch_pool));
7109 /* Got copyfrom data? Fix up the first segment to cover
7110 back to COPYFROM_REV + 1, and then prepend a new
7111 segment covering just COPYFROM_REV. */
7112 if (original_repos_relpath)
7114 svn_location_segment_t *new_segment =
7115 apr_pcalloc(result_pool, sizeof(*new_segment));
7117 new_segment->path = original_repos_relpath;
7118 new_segment->range_start = original_revision;
7119 new_segment->range_end = original_revision;
7120 svn_sort__array_insert(&new_segment, segments, 0);
7126 /* For each range in our requested range set, try to determine the
7127 path(s) associated with that range. */
7128 for (i = 0; i < merge_range_ts->nelts; i++)
7130 svn_merge_range_t *range =
7131 APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *);
7132 apr_array_header_t *merge_sources;
7134 if (SVN_IS_VALID_REVNUM(trim_revision))
7136 /* If the range predates the trim revision, discard it. */
7137 if (MAX(range->start, range->end) < trim_revision)
7140 /* If the range overlaps the trim revision, trim it. */
7141 if (range->start < trim_revision)
7142 range->start = trim_revision;
7143 if (range->end < trim_revision)
7144 range->end = trim_revision;
7147 /* Copy the resulting merge sources into master list thereof. */
7148 SVN_ERR(combine_range_with_segments(&merge_sources, range,
7149 segments, source_loc,
7151 apr_array_cat(*merge_sources_p, merge_sources);
7154 return SVN_NO_ERROR;
7157 /* Determine the normalized ranges to merge from a given line of history.
7159 Calculate the result by intersecting the list of location segments at
7160 which SOURCE_LOC existed along its line of history with the requested
7161 revision ranges in RANGES_TO_MERGE. RANGES_TO_MERGE is an array of
7162 (svn_opt_revision_range_t *) revision ranges. Use SOURCE_PATH_OR_URL to
7163 resolve any WC-relative revision specifiers (such as 'base') in
7166 Set *MERGE_SOURCES_P to an array of merge_source_t * objects, each
7167 describing a normalized range of revisions to be merged from the line
7168 history of SOURCE_LOC. Order the objects from oldest to youngest.
7170 RA_SESSION is an RA session open to the repository of SOURCE_LOC; it may
7171 be temporarily reparented within this function. Use RA_SESSION to find
7172 the location segments along the line of history of SOURCE_LOC.
7174 Allocate MERGE_SOURCES_P and its contents in RESULT_POOL.
7176 See `MERGEINFO MERGE SOURCE NORMALIZATION' for more on the
7177 background of this function.
7179 static svn_error_t *
7180 normalize_merge_sources(apr_array_header_t **merge_sources_p,
7181 const char *source_path_or_url,
7182 const svn_client__pathrev_t *source_loc,
7183 const apr_array_header_t *ranges_to_merge,
7184 svn_ra_session_t *ra_session,
7185 svn_client_ctx_t *ctx,
7186 apr_pool_t *result_pool,
7187 apr_pool_t *scratch_pool)
7189 const char *source_abspath_or_url;
7190 svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
7191 svn_rangelist_t *merge_range_ts;
7193 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7195 if(!svn_path_is_url(source_path_or_url))
7196 SVN_ERR(svn_dirent_get_absolute(&source_abspath_or_url, source_path_or_url,
7199 source_abspath_or_url = source_path_or_url;
7201 /* Create a list to hold svn_merge_range_t's. */
7202 merge_range_ts = apr_array_make(scratch_pool, ranges_to_merge->nelts,
7203 sizeof(svn_merge_range_t *));
7205 for (i = 0; i < ranges_to_merge->nelts; i++)
7207 svn_opt_revision_range_t *range
7208 = APR_ARRAY_IDX(ranges_to_merge, i, svn_opt_revision_range_t *);
7209 svn_merge_range_t mrange;
7211 svn_pool_clear(iterpool);
7213 /* Resolve revisions to real numbers, validating as we go. */
7214 if ((range->start.kind == svn_opt_revision_unspecified)
7215 || (range->end.kind == svn_opt_revision_unspecified))
7216 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
7217 _("Not all required revisions are specified"));
7219 SVN_ERR(svn_client__get_revision_number(&mrange.start, &youngest_rev,
7221 source_abspath_or_url,
7222 ra_session, &range->start,
7224 SVN_ERR(svn_client__get_revision_number(&mrange.end, &youngest_rev,
7226 source_abspath_or_url,
7227 ra_session, &range->end,
7230 /* If this isn't a no-op range... */
7231 if (mrange.start != mrange.end)
7233 /* ...then add it to the list. */
7234 mrange.inheritable = TRUE;
7235 APR_ARRAY_PUSH(merge_range_ts, svn_merge_range_t *)
7236 = svn_merge_range_dup(&mrange, scratch_pool);
7240 SVN_ERR(normalize_merge_sources_internal(
7241 merge_sources_p, source_loc,
7242 merge_range_ts, ra_session, ctx, result_pool, scratch_pool));
7244 svn_pool_destroy(iterpool);
7245 return SVN_NO_ERROR;
7249 /*-----------------------------------------------------------------------*/
7251 /*** Merge Workhorse Functions ***/
7253 /* Helper for do_directory_merge() and do_file_merge() which filters out a
7254 path's own natural history from the mergeinfo describing a merge.
7256 Given the natural history IMPLICIT_MERGEINFO of some wc merge target path,
7257 the repository-relative merge source path SOURCE_REL_PATH, and the
7258 requested merge range REQUESTED_RANGE from SOURCE_REL_PATH, remove any
7259 portion of REQUESTED_RANGE which is already described in
7260 IMPLICIT_MERGEINFO. Store the result in *FILTERED_RANGELIST.
7262 This function only filters natural history for mergeinfo that will be
7263 *added* during a forward merge. Removing natural history from explicit
7264 mergeinfo is harmless. If REQUESTED_RANGE describes a reverse merge,
7265 then *FILTERED_RANGELIST is simply populated with one range described
7266 by REQUESTED_RANGE. *FILTERED_RANGELIST is never NULL.
7268 Allocate *FILTERED_RANGELIST in POOL. */
7269 static svn_error_t *
7270 filter_natural_history_from_mergeinfo(svn_rangelist_t **filtered_rangelist,
7271 const char *source_rel_path,
7272 svn_mergeinfo_t implicit_mergeinfo,
7273 svn_merge_range_t *requested_range,
7276 /* Make the REQUESTED_RANGE into a rangelist. */
7277 svn_rangelist_t *requested_rangelist =
7278 svn_rangelist__initialize(requested_range->start, requested_range->end,
7279 requested_range->inheritable, pool);
7281 *filtered_rangelist = NULL;
7283 /* For forward merges: If the IMPLICIT_MERGEINFO already describes ranges
7284 associated with SOURCE_REL_PATH then filter those ranges out. */
7285 if (implicit_mergeinfo
7286 && (requested_range->start < requested_range->end))
7288 svn_rangelist_t *implied_rangelist =
7289 svn_hash_gets(implicit_mergeinfo, source_rel_path);
7291 if (implied_rangelist)
7292 SVN_ERR(svn_rangelist_remove(filtered_rangelist,
7294 requested_rangelist,
7298 /* If no filtering was performed the filtered rangelist is
7299 simply the requested rangelist.*/
7300 if (! (*filtered_rangelist))
7301 *filtered_rangelist = requested_rangelist;
7303 return SVN_NO_ERROR;
7306 /* Return a merge source representing the sub-range from START_REV to
7307 END_REV of SOURCE. SOURCE obeys the rules described in the
7308 'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file.
7309 The younger of START_REV and END_REV is inclusive while the older is
7312 Allocate the result structure in POOL but leave the URLs in it as shallow
7313 copies of the URLs in SOURCE.
7315 static merge_source_t *
7316 subrange_source(const merge_source_t *source,
7317 svn_revnum_t start_rev,
7318 svn_revnum_t end_rev,
7321 svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
7322 svn_boolean_t same_urls = (strcmp(source->loc1->url, source->loc2->url) == 0);
7323 svn_client__pathrev_t loc1 = *source->loc1;
7324 svn_client__pathrev_t loc2 = *source->loc2;
7326 /* For this function we require that the input source is 'ancestral'. */
7327 SVN_ERR_ASSERT_NO_RETURN(source->ancestral);
7328 SVN_ERR_ASSERT_NO_RETURN(start_rev != end_rev);
7330 loc1.rev = start_rev;
7334 if (is_rollback && (end_rev != source->loc2->rev))
7336 loc2.url = source->loc1->url;
7338 if ((! is_rollback) && (start_rev != source->loc1->rev))
7340 loc1.url = source->loc2->url;
7343 return merge_source_create(&loc1, &loc2, source->ancestral, pool);
7346 /* The single-file, simplified version of do_directory_merge(), which see for
7347 parameter descriptions.
7349 Additional parameters:
7351 If SOURCES_RELATED is set, the "left" and "right" sides of SOURCE are
7352 historically related (ancestors, uncles, second
7353 cousins thrice removed, etc...). (This is used to simulate the
7354 history checks that the repository logic does in the directory case.)
7356 If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
7357 is not NULL, then don't record the new mergeinfo on the TARGET_ABSPATH,
7358 but instead record it in RESULT_CATALOG, where the key is TARGET_ABSPATH
7359 and the value is the new mergeinfo for that path. Allocate additions
7360 to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
7362 CONFLICTED_RANGE is as documented for do_directory_merge().
7364 Note: MERGE_B->RA_SESSION1 must be associated with SOURCE->loc1->url and
7365 MERGE_B->RA_SESSION2 with SOURCE->loc2->url.
7367 static svn_error_t *
7368 do_file_merge(svn_mergeinfo_catalog_t result_catalog,
7369 single_range_conflict_report_t **conflict_report,
7370 const merge_source_t *source,
7371 const char *target_abspath,
7372 const svn_diff_tree_processor_t *processor,
7373 svn_boolean_t sources_related,
7374 svn_boolean_t squelch_mergeinfo_notifications,
7375 merge_cmd_baton_t *merge_b,
7376 apr_pool_t *result_pool,
7377 apr_pool_t *scratch_pool)
7379 svn_rangelist_t *remaining_ranges;
7380 svn_client_ctx_t *ctx = merge_b->ctx;
7381 svn_merge_range_t range;
7382 svn_mergeinfo_t target_mergeinfo;
7383 svn_boolean_t inherited = FALSE;
7384 svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
7385 const svn_client__pathrev_t *primary_src
7386 = is_rollback ? source->loc1 : source->loc2;
7387 svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
7388 svn_client__merge_path_t *merge_target = NULL;
7389 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7391 SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
7393 *conflict_report = NULL;
7395 /* Note that this is a single-file merge. */
7396 range.start = source->loc1->rev;
7397 range.end = source->loc2->rev;
7398 range.inheritable = TRUE;
7400 merge_target = svn_client__merge_path_create(target_abspath, scratch_pool);
7402 if (honor_mergeinfo)
7406 /* Fetch mergeinfo. */
7407 err = get_full_mergeinfo(&target_mergeinfo,
7408 &(merge_target->implicit_mergeinfo),
7409 &inherited, svn_mergeinfo_inherited,
7410 merge_b->ra_session1, target_abspath,
7411 MAX(source->loc1->rev, source->loc2->rev),
7412 MIN(source->loc1->rev, source->loc2->rev),
7413 ctx, scratch_pool, iterpool);
7417 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
7419 err = svn_error_createf(
7420 SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
7421 _("Invalid mergeinfo detected on merge target '%s', "
7422 "merge tracking not possible"),
7423 svn_dirent_local_style(target_abspath, scratch_pool));
7425 return svn_error_trace(err);
7428 /* Calculate remaining merges unless this is a record only merge.
7429 In that case the remaining range is the whole range described
7430 by SOURCE->rev1:rev2. */
7431 if (!merge_b->record_only)
7433 /* ### Bug? calculate_remaining_ranges() needs 'source' to adhere
7434 * to the requirements of 'MERGEINFO MERGE SOURCE NORMALIZATION'
7435 * here, but it doesn't appear to be guaranteed so. */
7436 SVN_ERR(calculate_remaining_ranges(NULL, merge_target,
7439 merge_b->implicit_src_gap, FALSE,
7440 merge_b->ra_session1,
7443 remaining_ranges = merge_target->remaining_ranges;
7445 /* We are honoring mergeinfo and this is not a simple record only
7446 merge which blindly records mergeinfo describing the merge of
7447 SOURCE->LOC1->URL@SOURCE->LOC1->REV through
7448 SOURCE->LOC2->URL@SOURCE->LOC2->REV. This means that the oldest
7449 and youngest revisions merged (as determined above by
7450 calculate_remaining_ranges) might differ from those described
7451 in SOURCE. To keep the '--- Merging *' notifications consistent
7452 with the '--- Recording mergeinfo *' notifications, we adjust
7453 RANGE to account for such changes. */
7454 if (remaining_ranges->nelts)
7456 svn_merge_range_t *adj_start_range =
7457 APR_ARRAY_IDX(remaining_ranges, 0, svn_merge_range_t *);
7458 svn_merge_range_t *adj_end_range =
7459 APR_ARRAY_IDX(remaining_ranges, remaining_ranges->nelts - 1,
7460 svn_merge_range_t *);
7461 range.start = adj_start_range->start;
7462 range.end = adj_end_range->end;
7467 /* The simple cases where our remaining range is SOURCE->rev1:rev2. */
7468 if (!honor_mergeinfo || merge_b->record_only)
7470 remaining_ranges = apr_array_make(scratch_pool, 1, sizeof(&range));
7471 APR_ARRAY_PUSH(remaining_ranges, svn_merge_range_t *) = ⦥
7474 if (!merge_b->record_only)
7476 svn_rangelist_t *ranges_to_merge = apr_array_copy(scratch_pool,
7478 const char *target_relpath = ""; /* relative to root of merge */
7480 if (source->ancestral)
7482 apr_array_header_t *child_with_mergeinfo;
7483 svn_client__merge_path_t *target_info;
7485 /* If we have ancestrally related sources and more than one
7486 range to merge, eliminate no-op ranges before going through
7487 the effort of downloading the many copies of the file
7488 required to do these merges (two copies per range). */
7489 if (remaining_ranges->nelts > 1)
7491 const char *old_sess_url;
7494 SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url,
7495 merge_b->ra_session1,
7498 err = remove_noop_merge_ranges(&ranges_to_merge,
7499 merge_b->ra_session1,
7500 remaining_ranges, scratch_pool);
7501 SVN_ERR(svn_error_compose_create(
7502 err, svn_ra_reparent(merge_b->ra_session1,
7503 old_sess_url, iterpool)));
7506 /* To support notify_merge_begin() initialize our
7507 CHILD_WITH_MERGEINFO. See the comment
7508 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */
7510 child_with_mergeinfo = apr_array_make(scratch_pool, 1,
7511 sizeof(svn_client__merge_path_t *));
7513 /* ### Create a fake copy of merge_target as we don't keep
7514 remaining_ranges in sync (yet). */
7515 target_info = apr_pcalloc(scratch_pool, sizeof(*target_info));
7517 target_info->abspath = merge_target->abspath;
7518 target_info->remaining_ranges = ranges_to_merge;
7520 APR_ARRAY_PUSH(child_with_mergeinfo, svn_client__merge_path_t *)
7523 /* And store in baton to allow using it from notify_merge_begin() */
7524 merge_b->notify_begin.nodes_with_mergeinfo = child_with_mergeinfo;
7527 while (ranges_to_merge->nelts > 0)
7529 svn_merge_range_t *r = APR_ARRAY_IDX(ranges_to_merge, 0,
7530 svn_merge_range_t *);
7531 const merge_source_t *real_source;
7532 const char *left_file, *right_file;
7533 apr_hash_t *left_props, *right_props;
7534 const svn_diff_source_t *left_source;
7535 const svn_diff_source_t *right_source;
7537 svn_pool_clear(iterpool);
7539 /* Ensure any subsequent drives gets their own notification. */
7540 merge_b->notify_begin.last_abspath = NULL;
7542 /* While we currently don't allow it, in theory we could be
7543 fetching two fulltexts from two different repositories here. */
7544 if (source->ancestral)
7545 real_source = subrange_source(source, r->start, r->end, iterpool);
7547 real_source = source;
7548 SVN_ERR(single_file_merge_get_file(&left_file, &left_props,
7549 merge_b->ra_session1,
7552 iterpool, iterpool));
7553 SVN_ERR(single_file_merge_get_file(&right_file, &right_props,
7554 merge_b->ra_session2,
7557 iterpool, iterpool));
7558 /* Calculate sources for the diff processor */
7559 left_source = svn_diff__source_create(r->start, iterpool);
7560 right_source = svn_diff__source_create(r->end, iterpool);
7563 /* If the sources are related or we're ignoring ancestry in diffs,
7564 do a text-n-props merge; otherwise, do a delete-n-add merge. */
7565 if (! (merge_b->diff_ignore_ancestry || sources_related))
7567 struct merge_dir_baton_t dir_baton;
7571 /* Initialize minimal dir baton to allow calculating 'R'eplace
7572 from 'D'elete + 'A'dd. */
7574 memset(&dir_baton, 0, sizeof(dir_baton));
7575 dir_baton.pool = iterpool;
7576 dir_baton.tree_conflict_reason = CONFLICT_REASON_NONE;
7577 dir_baton.tree_conflict_action = svn_wc_conflict_action_edit;
7578 dir_baton.skip_reason = svn_wc_notify_state_unknown;
7583 SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7585 NULL /* right_source */,
7586 NULL /* copyfrom_source */,
7589 iterpool, iterpool));
7591 SVN_ERR(processor->file_deleted(target_relpath,
7599 /* ...plus add... */
7602 SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7603 NULL /* left_source */,
7605 NULL /* copyfrom_source */,
7608 iterpool, iterpool));
7610 SVN_ERR(processor->file_added(target_relpath,
7611 NULL /* copyfrom_source */,
7613 NULL /* copyfrom_file */,
7615 NULL /* copyfrom_props */,
7620 /* ... equals replace. */
7624 void *file_baton = NULL;
7625 svn_boolean_t skip = FALSE;
7626 apr_array_header_t *propchanges;
7629 /* Deduce property diffs. */
7630 SVN_ERR(svn_prop_diffs(&propchanges, right_props, left_props,
7633 SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7636 NULL /* copyfrom_source */,
7637 NULL /* dir_baton */,
7639 iterpool, iterpool));
7641 SVN_ERR(processor->file_changed(target_relpath,
7648 TRUE /* file changed */,
7655 if (is_path_conflicted_by_merge(merge_b))
7657 merge_source_t *remaining_range = NULL;
7659 if (real_source->loc2->rev != source->loc2->rev)
7660 remaining_range = subrange_source(source,
7661 real_source->loc2->rev,
7664 *conflict_report = single_range_conflict_report_create(
7665 real_source, remaining_range, result_pool);
7667 /* Only record partial mergeinfo if only a partial merge was
7668 performed before a conflict was encountered. */
7673 /* Now delete the just merged range from the hash
7674 (This list is used from notify_merge_begin)
7676 Directory merges use remove_first_range_from_remaining_ranges() */
7677 svn_sort__array_delete(ranges_to_merge, 0, 1);
7679 merge_b->notify_begin.last_abspath = NULL;
7680 } /* !merge_b->record_only */
7682 /* Record updated WC mergeinfo to account for our new merges, minus
7683 any unresolved conflicts and skips. We use the original
7684 REMAINING_RANGES here because we want to record all the requested
7685 merge ranges, include the noop ones. */
7686 if (RECORD_MERGEINFO(merge_b) && remaining_ranges->nelts)
7688 const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
7690 svn_rangelist_t *filtered_rangelist;
7692 /* Filter any ranges from TARGET_WCPATH's own history, there is no
7693 need to record this explicitly in mergeinfo, it is already part
7694 of TARGET_WCPATH's natural history (implicit mergeinfo). */
7695 SVN_ERR(filter_natural_history_from_mergeinfo(
7696 &filtered_rangelist,
7698 merge_target->implicit_mergeinfo,
7702 /* Only record mergeinfo if there is something other than
7703 self-referential mergeinfo, but don't record mergeinfo if
7704 TARGET_WCPATH was skipped. */
7705 if (filtered_rangelist->nelts
7706 && (apr_hash_count(merge_b->skipped_abspaths) == 0))
7708 apr_hash_t *merges = apr_hash_make(iterpool);
7710 /* If merge target has inherited mergeinfo set it before
7711 recording the first merge range. */
7713 SVN_ERR(svn_client__record_wc_mergeinfo(target_abspath,
7718 svn_hash_sets(merges, target_abspath, filtered_rangelist);
7720 if (!squelch_mergeinfo_notifications)
7722 /* Notify that we are recording mergeinfo describing a merge. */
7723 svn_merge_range_t n_range;
7725 SVN_ERR(svn_mergeinfo__get_range_endpoints(
7726 &n_range.end, &n_range.start, merges, iterpool));
7727 n_range.inheritable = TRUE;
7728 notify_mergeinfo_recording(target_abspath, &n_range,
7729 merge_b->ctx, iterpool);
7732 SVN_ERR(update_wc_mergeinfo(result_catalog, target_abspath,
7733 mergeinfo_path, merges, is_rollback,
7738 merge_b->notify_begin.nodes_with_mergeinfo = NULL;
7740 svn_pool_destroy(iterpool);
7742 return SVN_NO_ERROR;
7745 /* Helper for do_directory_merge() to handle the case where a merge editor
7746 drive adds explicit mergeinfo to a path which didn't have any explicit
7747 mergeinfo previously.
7749 MERGE_B is cascaded from the argument of the same
7750 name in do_directory_merge(). Should be called only after
7751 do_directory_merge() has called populate_remaining_ranges() and populated
7752 the remaining_ranges field of each child in
7753 CHILDREN_WITH_MERGEINFO (i.e. the remaining_ranges fields can be
7754 empty but never NULL).
7756 If MERGE_B->DRY_RUN is true do nothing, if it is false then
7757 for each path (if any) in MERGE_B->PATHS_WITH_NEW_MERGEINFO merge that
7758 path's inherited mergeinfo (if any) with its working explicit mergeinfo
7759 and set that as the path's new explicit mergeinfo. Then add an
7760 svn_client__merge_path_t * element representing the path to
7761 CHILDREN_WITH_MERGEINFO if it isn't already present. All fields
7762 in any elements added to CHILDREN_WITH_MERGEINFO are initialized
7763 to FALSE/NULL with the exception of 'path' and 'remaining_ranges'. The
7764 latter is set to a rangelist equal to the remaining_ranges of the path's
7765 nearest path-wise ancestor in CHILDREN_WITH_MERGEINFO.
7767 Any elements added to CHILDREN_WITH_MERGEINFO are allocated
7769 static svn_error_t *
7770 process_children_with_new_mergeinfo(merge_cmd_baton_t *merge_b,
7771 apr_array_header_t *children_with_mergeinfo,
7774 apr_pool_t *iterpool;
7775 apr_hash_index_t *hi;
7777 if (!merge_b->paths_with_new_mergeinfo || merge_b->dry_run)
7778 return SVN_NO_ERROR;
7780 /* Iterate over each path with explicit mergeinfo added by the merge. */
7781 iterpool = svn_pool_create(pool);
7782 for (hi = apr_hash_first(pool, merge_b->paths_with_new_mergeinfo);
7784 hi = apr_hash_next(hi))
7786 const char *abspath_with_new_mergeinfo = svn__apr_hash_index_key(hi);
7787 svn_mergeinfo_t path_inherited_mergeinfo;
7788 svn_mergeinfo_t path_explicit_mergeinfo;
7789 svn_client__merge_path_t *new_child;
7791 svn_pool_clear(iterpool);
7793 /* Note: We could skip recording inherited mergeinfo here if this path
7794 was added (with preexisting mergeinfo) by the merge. That's actually
7795 more correct, since the inherited mergeinfo likely describes
7796 non-existent or unrelated merge history, but it's not quite so simple
7797 as that, see http://subversion.tigris.org/issues/show_bug.cgi?id=4309
7800 /* Get the path's new explicit mergeinfo... */
7801 SVN_ERR(svn_client__get_wc_mergeinfo(&path_explicit_mergeinfo, NULL,
7802 svn_mergeinfo_explicit,
7803 abspath_with_new_mergeinfo,
7806 iterpool, iterpool));
7807 /* ...there *should* always be explicit mergeinfo at this point
7808 but you can't be too careful. */
7809 if (path_explicit_mergeinfo)
7811 /* Get the mergeinfo the path would have inherited before
7813 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(
7814 &path_inherited_mergeinfo,
7817 svn_mergeinfo_nearest_ancestor, /* We only want inherited MI */
7818 merge_b->ra_session2,
7819 abspath_with_new_mergeinfo,
7823 /* If the path inherited any mergeinfo then merge that with the
7824 explicit mergeinfo and record the result as the path's new
7825 explicit mergeinfo. */
7826 if (path_inherited_mergeinfo)
7828 SVN_ERR(svn_mergeinfo_merge2(path_explicit_mergeinfo,
7829 path_inherited_mergeinfo,
7830 iterpool, iterpool));
7831 SVN_ERR(svn_client__record_wc_mergeinfo(
7832 abspath_with_new_mergeinfo,
7833 path_explicit_mergeinfo,
7834 FALSE, merge_b->ctx, iterpool));
7837 /* If the path is not in CHILDREN_WITH_MERGEINFO then add it. */
7839 get_child_with_mergeinfo(children_with_mergeinfo,
7840 abspath_with_new_mergeinfo);
7843 const svn_client__merge_path_t *parent
7844 = find_nearest_ancestor(children_with_mergeinfo,
7845 FALSE, abspath_with_new_mergeinfo);
7847 = svn_client__merge_path_create(abspath_with_new_mergeinfo,
7850 /* If path_with_new_mergeinfo is the merge target itself
7851 then it should already be in
7852 CHILDREN_WITH_MERGEINFO per the criteria of
7853 get_mergeinfo_paths() and we shouldn't be in this block.
7854 If path_with_new_mergeinfo is a subtree then it must have
7855 a parent in CHILDREN_WITH_MERGEINFO if only
7856 the merge target itself...so if we don't find a parent
7857 the caller has done something quite wrong. */
7858 SVN_ERR_ASSERT(parent);
7859 SVN_ERR_ASSERT(parent->remaining_ranges);
7861 /* Set the path's remaining_ranges equal to its parent's. */
7862 new_child->remaining_ranges = svn_rangelist_dup(
7863 parent->remaining_ranges, pool);
7864 insert_child_to_merge(children_with_mergeinfo, new_child, pool);
7868 svn_pool_destroy(iterpool);
7870 return SVN_NO_ERROR;
7873 /* Return true if any path in SUBTREES is equal to, or is a subtree of,
7874 LOCAL_ABSPATH. Return false otherwise. The keys of SUBTREES are
7875 (const char *) absolute paths and its values are irrelevant.
7876 If SUBTREES is NULL return false. */
7877 static svn_boolean_t
7878 path_is_subtree(const char *local_abspath,
7879 apr_hash_t *subtrees,
7884 apr_hash_index_t *hi;
7886 for (hi = apr_hash_first(pool, subtrees);
7887 hi; hi = apr_hash_next(hi))
7889 const char *path_touched_by_merge = svn__apr_hash_index_key(hi);
7890 if (svn_dirent_is_ancestor(local_abspath, path_touched_by_merge))
7897 /* Return true if any merged, skipped, added or tree-conflicted path
7898 recorded in MERGE_B is equal to, or is a subtree of LOCAL_ABSPATH. Return
7901 ### Why not text- or prop-conflicted paths? Are such paths guaranteed
7902 to be recorded as 'merged' or 'skipped' or 'added', perhaps?
7904 static svn_boolean_t
7905 subtree_touched_by_merge(const char *local_abspath,
7906 merge_cmd_baton_t *merge_b,
7909 return (path_is_subtree(local_abspath, merge_b->merged_abspaths, pool)
7910 || path_is_subtree(local_abspath, merge_b->skipped_abspaths, pool)
7911 || path_is_subtree(local_abspath, merge_b->added_abspaths, pool)
7912 || path_is_subtree(local_abspath, merge_b->tree_conflicted_abspaths,
7916 /* Helper for do_directory_merge() when performing mergeinfo unaware merges.
7918 Merge the SOURCE diff into TARGET_DIR_WCPATH.
7920 SOURCE, DEPTH, NOTIFY_B, and MERGE_B
7921 are all cascaded from do_directory_merge's arguments of the same names.
7923 CONFLICT_REPORT is as documented for do_directory_merge().
7925 NOTE: This is a very thin wrapper around drive_merge_report_editor() and
7926 exists only to populate CHILDREN_WITH_MERGEINFO with the single element
7927 expected during mergeinfo unaware merges.
7929 static svn_error_t *
7930 do_mergeinfo_unaware_dir_merge(single_range_conflict_report_t **conflict_report,
7931 const merge_source_t *source,
7932 const char *target_dir_wcpath,
7933 apr_array_header_t *children_with_mergeinfo,
7934 const svn_diff_tree_processor_t *processor,
7936 merge_cmd_baton_t *merge_b,
7937 apr_pool_t *result_pool,
7938 apr_pool_t *scratch_pool)
7940 /* Initialize CHILDREN_WITH_MERGEINFO and populate it with
7941 one element describing the merge of SOURCE->rev1:rev2 to
7942 TARGET_DIR_WCPATH. */
7943 svn_client__merge_path_t *item
7944 = svn_client__merge_path_create(target_dir_wcpath, scratch_pool);
7946 *conflict_report = NULL;
7947 item->remaining_ranges = svn_rangelist__initialize(source->loc1->rev,
7949 TRUE, scratch_pool);
7950 APR_ARRAY_PUSH(children_with_mergeinfo,
7951 svn_client__merge_path_t *) = item;
7952 SVN_ERR(drive_merge_report_editor(target_dir_wcpath,
7954 NULL, processor, depth,
7955 merge_b, scratch_pool));
7956 if (is_path_conflicted_by_merge(merge_b))
7958 *conflict_report = single_range_conflict_report_create(
7959 source, NULL, result_pool);
7961 return SVN_NO_ERROR;
7964 /* A svn_log_entry_receiver_t baton for log_find_operative_subtree_revs(). */
7965 typedef struct log_find_operative_subtree_baton_t
7967 /* Mapping of const char * absolute working copy paths to those
7968 path's const char * repos absolute paths. */
7969 apr_hash_t *operative_children;
7971 /* As per the arguments of the same name to
7972 get_operative_immediate_children(). */
7973 const char *merge_source_fspath;
7974 const char *merge_target_abspath;
7976 svn_wc_context_t *wc_ctx;
7978 /* A pool to allocate additions to the hashes in. */
7979 apr_pool_t *result_pool;
7980 } log_find_operative_subtree_baton_t;
7982 /* A svn_log_entry_receiver_t callback for
7983 get_inoperative_immediate_children(). */
7984 static svn_error_t *
7985 log_find_operative_subtree_revs(void *baton,
7986 svn_log_entry_t *log_entry,
7989 log_find_operative_subtree_baton_t *log_baton = baton;
7990 apr_hash_index_t *hi;
7991 apr_pool_t *iterpool;
7993 /* It's possible that authz restrictions on the merge source prevent us
7994 from knowing about any of the changes for LOG_ENTRY->REVISION. */
7995 if (!log_entry->changed_paths2)
7996 return SVN_NO_ERROR;
7998 iterpool = svn_pool_create(pool);
8000 for (hi = apr_hash_first(pool, log_entry->changed_paths2);
8002 hi = apr_hash_next(hi))
8004 const char *path = svn__apr_hash_index_key(hi);
8005 svn_log_changed_path2_t *change = svn__apr_hash_index_val(hi);
8009 const char *potential_child;
8010 const char *rel_path =
8011 svn_fspath__skip_ancestor(log_baton->merge_source_fspath, path);
8013 /* Some affected paths might be the root of the merge source or
8014 entirely outside our subtree of interest. In either case they
8015 are not operative *immediate* children. */
8016 if (rel_path == NULL
8017 || rel_path[0] == '\0')
8020 svn_pool_clear(iterpool);
8022 child = svn_relpath_dirname(rel_path, iterpool);
8023 if (child[0] == '\0')
8025 /* The svn_log_changed_path2_t.node_kind members in
8026 LOG_ENTRY->CHANGED_PATHS2 may be set to
8027 svn_node_unknown, see svn_log_changed_path2_t and
8028 svn_fs_paths_changed2. In that case we check the
8029 type of the corresponding subtree in the merge
8031 svn_node_kind_t node_kind;
8033 if (change->node_kind == svn_node_unknown)
8035 const char *wc_child_abspath =
8036 svn_dirent_join(log_baton->merge_target_abspath,
8037 rel_path, iterpool);
8039 SVN_ERR(svn_wc_read_kind2(&node_kind, log_baton->wc_ctx,
8040 wc_child_abspath, FALSE, FALSE,
8045 node_kind = change->node_kind;
8048 /* We only care about immediate directory children if
8049 DEPTH is svn_depth_files. */
8050 if (log_baton->depth == svn_depth_files
8051 && node_kind != svn_node_dir)
8054 /* If depth is svn_depth_immediates, then we only care
8055 about changes to proper subtrees of PATH. If the change
8056 is to PATH itself then PATH is within the operational
8057 depth of the merge. */
8058 if (log_baton->depth == svn_depth_immediates)
8064 potential_child = svn_dirent_join(log_baton->merge_target_abspath,
8067 if (change->action == 'A'
8068 || !svn_hash_gets(log_baton->operative_children,
8071 svn_hash_sets(log_baton->operative_children,
8072 apr_pstrdup(log_baton->result_pool,
8074 apr_pstrdup(log_baton->result_pool, path));
8078 svn_pool_destroy(iterpool);
8079 return SVN_NO_ERROR;
8082 /* Find immediate subtrees of MERGE_TARGET_ABSPATH which would have
8083 additional differences applied if record_mergeinfo_for_dir_merge() were
8084 recording mergeinfo describing a merge at svn_depth_infinity, rather
8085 than at DEPTH (which is assumed to be shallow; if
8086 DEPTH == svn_depth_infinity then this function does nothing beyond
8087 setting *OPERATIVE_CHILDREN to an empty hash).
8089 MERGE_SOURCE_FSPATH is the absolute repository path of the merge
8090 source. OLDEST_REV and YOUNGEST_REV are the revisions merged from
8091 MERGE_SOURCE_FSPATH to MERGE_TARGET_ABSPATH.
8093 RA_SESSION points to MERGE_SOURCE_FSPATH.
8095 Set *OPERATIVE_CHILDREN to a hash (mapping const char * absolute
8096 working copy paths to those path's const char * repos absolute paths)
8097 containing all the immediate subtrees of MERGE_TARGET_ABSPATH which would
8098 have a different diff applied if MERGE_SOURCE_FSPATH
8099 -r(OLDEST_REV - 1):YOUNGEST_REV were merged to MERGE_TARGET_ABSPATH at
8100 svn_depth_infinity rather than DEPTH.
8102 RESULT_POOL is used to allocate the contents of *OPERATIVE_CHILDREN.
8103 SCRATCH_POOL is used for temporary allocations. */
8104 static svn_error_t *
8105 get_operative_immediate_children(apr_hash_t **operative_children,
8106 const char *merge_source_fspath,
8107 svn_revnum_t oldest_rev,
8108 svn_revnum_t youngest_rev,
8109 const char *merge_target_abspath,
8111 svn_wc_context_t *wc_ctx,
8112 svn_ra_session_t *ra_session,
8113 apr_pool_t *result_pool,
8114 apr_pool_t *scratch_pool)
8116 log_find_operative_subtree_baton_t log_baton;
8118 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
8119 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
8120 SVN_ERR_ASSERT(oldest_rev <= youngest_rev);
8122 *operative_children = apr_hash_make(result_pool);
8124 if (depth == svn_depth_infinity)
8125 return SVN_NO_ERROR;
8127 /* Now remove any paths from *OPERATIVE_CHILDREN that are inoperative when
8128 merging MERGE_SOURCE_REPOS_PATH -r(OLDEST_REV - 1):YOUNGEST_REV to
8129 MERGE_TARGET_ABSPATH at --depth infinity. */
8130 log_baton.operative_children = *operative_children;
8131 log_baton.merge_source_fspath = merge_source_fspath;
8132 log_baton.merge_target_abspath = merge_target_abspath;
8133 log_baton.depth = depth;
8134 log_baton.wc_ctx = wc_ctx;
8135 log_baton.result_pool = result_pool;
8137 SVN_ERR(get_log(ra_session, "", youngest_rev, oldest_rev,
8138 TRUE, /* discover_changed_paths */
8139 log_find_operative_subtree_revs,
8140 &log_baton, scratch_pool));
8142 return SVN_NO_ERROR;
8145 /* Helper for record_mergeinfo_for_dir_merge(): Identify which elements of
8146 CHILDREN_WITH_MERGEINFO need new mergeinfo set to accurately
8147 describe a merge, what inheritance type such new mergeinfo should have,
8148 and what subtrees can be ignored altogether.
8150 For each svn_client__merge_path_t CHILD in CHILDREN_WITH_MERGEINFO,
8151 set CHILD->RECORD_MERGEINFO and CHILD->RECORD_NONINHERITABLE to true
8152 if the subtree needs mergeinfo to describe the merge and if that
8153 mergeinfo should be non-inheritable respectively.
8155 If OPERATIVE_MERGE is true, then the merge being described is operative
8156 as per subtree_touched_by_merge(). OPERATIVE_MERGE is false otherwise.
8158 MERGED_RANGE, MERGEINFO_FSPATH, DEPTH, NOTIFY_B, and MERGE_B are all
8159 cascaded from record_mergeinfo_for_dir_merge's arguments of the same
8162 SCRATCH_POOL is used for temporary allocations.
8164 static svn_error_t *
8165 flag_subtrees_needing_mergeinfo(svn_boolean_t operative_merge,
8166 const svn_merge_range_t *merged_range,
8167 apr_array_header_t *children_with_mergeinfo,
8168 const char *mergeinfo_fspath,
8170 merge_cmd_baton_t *merge_b,
8171 apr_pool_t *scratch_pool)
8173 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
8175 apr_hash_t *operative_immediate_children = NULL;
8177 assert(! merge_b->dry_run);
8179 if (!merge_b->record_only
8180 && merged_range->start <= merged_range->end
8181 && (depth < svn_depth_infinity))
8182 SVN_ERR(get_operative_immediate_children(
8183 &operative_immediate_children,
8184 mergeinfo_fspath, merged_range->start + 1, merged_range->end,
8185 merge_b->target->abspath, depth, merge_b->ctx->wc_ctx,
8186 merge_b->ra_session1, scratch_pool, iterpool));
8188 /* Issue #4056: Walk NOTIFY_B->CHILDREN_WITH_MERGEINFO reverse depth-first
8189 order. This way each child knows if it has operative missing/switched
8190 children which necessitates non-inheritable mergeinfo. */
8191 for (i = children_with_mergeinfo->nelts - 1; i >= 0; i--)
8193 svn_client__merge_path_t *child =
8194 APR_ARRAY_IDX(children_with_mergeinfo, i,
8195 svn_client__merge_path_t *);
8197 /* Can't record mergeinfo on something that isn't here. */
8201 /* Verify that remove_children_with_deleted_mergeinfo() did its job */
8203 ||! merge_b->paths_with_deleted_mergeinfo
8204 || !svn_hash_gets(merge_b->paths_with_deleted_mergeinfo,
8207 /* Don't record mergeinfo on skipped paths. */
8208 if (svn_hash_gets(merge_b->skipped_abspaths, child->abspath))
8211 /* ### ptb: Yes, we could combine the following into a single
8212 ### conditional, but clarity would suffer (even more than
8213 ### it does now). */
8216 /* Always record mergeinfo on the merge target. */
8217 child->record_mergeinfo = TRUE;
8219 else if (merge_b->record_only && !merge_b->reintegrate_merge)
8221 /* Always record mergeinfo for --record-only merges. */
8222 child->record_mergeinfo = TRUE;
8224 else if (child->immediate_child_dir
8225 && !child->pre_merge_mergeinfo
8226 && operative_immediate_children
8227 && svn_hash_gets(operative_immediate_children, child->abspath))
8229 /* We must record mergeinfo on those issue #3642 children
8230 that are operative at a greater depth. */
8231 child->record_mergeinfo = TRUE;
8235 && subtree_touched_by_merge(child->abspath, merge_b, iterpool))
8237 svn_pool_clear(iterpool);
8239 /* This subtree was affected by the merge. */
8240 child->record_mergeinfo = TRUE;
8242 /* Were any CHILD's missing children skipped by the merge?
8243 If not, then CHILD's missing children don't need to be
8244 considered when recording mergeinfo describing the merge. */
8245 if (! merge_b->reintegrate_merge
8246 && child->missing_child
8247 && !path_is_subtree(child->abspath,
8248 merge_b->skipped_abspaths,
8251 child->missing_child = FALSE;
8254 /* If CHILD has an immediate switched child or children and
8255 none of these were touched by the merge, then we don't need
8256 need to do any special handling of those switched subtrees
8257 (e.g. record non-inheritable mergeinfo) when recording
8258 mergeinfo describing the merge. */
8259 if (child->switched_child)
8262 svn_boolean_t operative_switched_child = FALSE;
8265 j < children_with_mergeinfo->nelts;
8268 svn_client__merge_path_t *potential_child =
8269 APR_ARRAY_IDX(children_with_mergeinfo, j,
8270 svn_client__merge_path_t *);
8271 if (!svn_dirent_is_ancestor(child->abspath,
8272 potential_child->abspath))
8275 /* POTENTIAL_CHILD is a subtree of CHILD, but is it
8276 an immediate child? */
8277 if (strcmp(child->abspath,
8278 svn_dirent_dirname(potential_child->abspath,
8282 if (potential_child->switched
8283 && potential_child->record_mergeinfo)
8285 operative_switched_child = TRUE;
8290 /* Can we treat CHILD as if it has no switched children? */
8291 if (! operative_switched_child)
8292 child->switched_child = FALSE;
8296 if (child->record_mergeinfo)
8298 /* We need to record mergeinfo, but should that mergeinfo be
8300 svn_node_kind_t path_kind;
8301 SVN_ERR(svn_wc_read_kind2(&path_kind, merge_b->ctx->wc_ctx,
8302 child->abspath, FALSE, FALSE, iterpool));
8304 /* Only directories can have non-inheritable mergeinfo. */
8305 if (path_kind == svn_node_dir)
8307 /* There are two general cases where non-inheritable mergeinfo
8310 1) There merge target has missing subtrees (due to authz
8311 restrictions, switched subtrees, or a shallow working
8314 2) The operational depth of the merge itself is shallow. */
8316 /* We've already determined the first case. */
8317 child->record_noninheritable =
8318 child->missing_child || child->switched_child;
8320 /* The second case requires a bit more work. */
8323 /* If CHILD is the root of the merge target and the
8324 operational depth is empty or files, then the mere
8325 existence of operative immediate children means we
8326 must record non-inheritable mergeinfo.
8328 ### What about svn_depth_immediates? In that case
8329 ### the merge target needs only normal inheritable
8330 ### mergeinfo and the target's immediate children will
8331 ### get non-inheritable mergeinfo, assuming they
8332 ### need even that. */
8333 if (depth < svn_depth_immediates
8334 && operative_immediate_children
8335 && apr_hash_count(operative_immediate_children))
8336 child->record_noninheritable = TRUE;
8338 else if (depth == svn_depth_immediates)
8340 /* An immediate directory child of the merge target, which
8341 was affected by a --depth=immediates merge, needs
8342 non-inheritable mergeinfo. */
8343 if (svn_hash_gets(operative_immediate_children,
8345 child->record_noninheritable = TRUE;
8349 else /* child->record_mergeinfo */
8351 /* If CHILD is in NOTIFY_B->CHILDREN_WITH_MERGEINFO simply
8352 because it had no explicit mergeinfo of its own at the
8353 start of the merge but is the child of of some path with
8354 non-inheritable mergeinfo, then the explicit mergeinfo it
8355 has *now* was set by get_mergeinfo_paths() -- see criteria
8356 3 in that function's doc string. So since CHILD->ABSPATH
8357 was not touched by the merge we can remove the
8359 if (child->child_of_noninheritable)
8360 SVN_ERR(svn_client__record_wc_mergeinfo(child->abspath,
8367 svn_pool_destroy(iterpool);
8368 return SVN_NO_ERROR;
8371 /* Helper for do_directory_merge().
8373 If RESULT_CATALOG is NULL then record mergeinfo describing a merge of
8374 MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
8375 MERGEINFO_FSPATH to the merge target (and possibly its subtrees) described
8376 by NOTIFY_B->CHILDREN_WITH_MERGEINFO -- see the global comment
8377 'THE CHILDREN_WITH_MERGEINFO ARRAY'. Obviously this should only
8378 be called if recording mergeinfo -- see doc string for RECORD_MERGEINFO().
8380 If RESULT_CATALOG is not NULL, then don't record the new mergeinfo on the
8381 WC, but instead record it in RESULT_CATALOG, where the keys are absolute
8382 working copy paths and the values are the new mergeinfos for each.
8383 Allocate additions to RESULT_CATALOG in pool which RESULT_CATALOG was
8386 DEPTH, NOTIFY_B, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS are all
8387 cascaded from do_directory_merge's arguments of the same names.
8389 SCRATCH_POOL is used for temporary allocations.
8391 static svn_error_t *
8392 record_mergeinfo_for_dir_merge(svn_mergeinfo_catalog_t result_catalog,
8393 const svn_merge_range_t *merged_range,
8394 const char *mergeinfo_fspath,
8395 apr_array_header_t *children_with_mergeinfo,
8397 svn_boolean_t squelch_mergeinfo_notifications,
8398 merge_cmd_baton_t *merge_b,
8399 apr_pool_t *scratch_pool)
8402 svn_boolean_t is_rollback = (merged_range->start > merged_range->end);
8403 svn_boolean_t operative_merge;
8405 /* Update the WC mergeinfo here to account for our new
8406 merges, minus any unresolved conflicts and skips. */
8408 /* We need a scratch pool for iterations below. */
8409 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
8411 svn_merge_range_t range = *merged_range;
8413 assert(! merge_b->dry_run);
8415 /* Regardless of what subtrees in MERGE_B->target->abspath might be missing
8416 could this merge have been operative? */
8417 operative_merge = subtree_touched_by_merge(merge_b->target->abspath,
8420 /* If this couldn't be an operative merge then don't bother with
8421 the added complexity (and user confusion) of non-inheritable ranges.
8422 There is no harm in subtrees inheriting inoperative mergeinfo. */
8423 if (!operative_merge)
8424 range.inheritable = TRUE;
8426 /* Remove absent children at or under MERGE_B->target->abspath from
8427 NOTIFY_B->CHILDREN_WITH_MERGEINFO
8428 before we calculate the merges performed. */
8429 remove_absent_children(merge_b->target->abspath,
8430 children_with_mergeinfo);
8432 /* Determine which subtrees of interest need mergeinfo recorded... */
8433 SVN_ERR(flag_subtrees_needing_mergeinfo(operative_merge, &range,
8434 children_with_mergeinfo,
8435 mergeinfo_fspath, depth,
8436 merge_b, iterpool));
8438 /* ...and then record it. */
8439 for (i = 0; i < children_with_mergeinfo->nelts; i++)
8441 const char *child_repos_path;
8442 const char *child_merge_src_fspath;
8443 svn_rangelist_t *child_merge_rangelist;
8444 apr_hash_t *child_merges;
8445 svn_client__merge_path_t *child =
8446 APR_ARRAY_IDX(children_with_mergeinfo, i,
8447 svn_client__merge_path_t *);
8448 SVN_ERR_ASSERT(child);
8450 svn_pool_clear(iterpool);
8452 if (child->record_mergeinfo)
8454 child_repos_path = svn_dirent_skip_ancestor(merge_b->target->abspath,
8456 SVN_ERR_ASSERT(child_repos_path != NULL);
8457 child_merge_src_fspath = svn_fspath__join(mergeinfo_fspath,
8460 /* Filter any ranges from each child's natural history before
8461 setting mergeinfo describing the merge. */
8462 SVN_ERR(filter_natural_history_from_mergeinfo(
8463 &child_merge_rangelist, child_merge_src_fspath,
8464 child->implicit_mergeinfo, &range, iterpool));
8466 if (child_merge_rangelist->nelts == 0)
8469 if (!squelch_mergeinfo_notifications)
8471 /* If the merge source has a gap, then don't mention
8472 those gap revisions in the notification. */
8473 remove_source_gap(&range, merge_b->implicit_src_gap);
8474 notify_mergeinfo_recording(child->abspath, &range,
8475 merge_b->ctx, iterpool);
8478 /* If we are here we know we will be recording some mergeinfo, but
8479 before we do, set override mergeinfo on skipped paths so they
8480 don't incorrectly inherit the mergeinfo we are about to set. */
8482 SVN_ERR(record_skips_in_mergeinfo(mergeinfo_fspath,
8483 child_merge_rangelist,
8484 is_rollback, merge_b, iterpool));
8486 /* We may need to record non-inheritable mergeinfo that applies
8487 only to CHILD->ABSPATH. */
8488 if (child->record_noninheritable)
8489 svn_rangelist__set_inheritance(child_merge_rangelist, FALSE);
8491 /* If CHILD has inherited mergeinfo set it before
8492 recording the first merge range. */
8493 if (child->inherited_mergeinfo)
8494 SVN_ERR(svn_client__record_wc_mergeinfo(
8496 child->pre_merge_mergeinfo,
8497 FALSE, merge_b->ctx,
8499 if (merge_b->implicit_src_gap)
8501 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
8502 so it will work with the svn_rangelist_remove API. */
8504 SVN_ERR(svn_rangelist_reverse(child_merge_rangelist,
8507 SVN_ERR(svn_rangelist_remove(&child_merge_rangelist,
8508 merge_b->implicit_src_gap,
8509 child_merge_rangelist, FALSE,
8512 SVN_ERR(svn_rangelist_reverse(child_merge_rangelist,
8516 child_merges = apr_hash_make(iterpool);
8520 If we are describing a forward merge, then the naive mergeinfo
8521 defined by MERGE_SOURCE_PATH:MERGED_RANGE->START:
8522 MERGE_SOURCE_PATH:MERGED_RANGE->END may contain non-existent
8523 path-revs or may describe other lines of history. We must
8524 remove these invalid portion(s) before recording mergeinfo
8525 describing the merge.
8529 If CHILD is the merge target we know that
8530 MERGE_SOURCE_PATH:MERGED_RANGE->END exists. Further, if there
8531 were no copies in MERGE_SOURCE_PATH's history going back to
8532 RANGE->START then we know that
8533 MERGE_SOURCE_PATH:MERGED_RANGE->START exists too and the two
8534 describe an unbroken line of history, and thus
8535 MERGE_SOURCE_PATH:MERGED_RANGE->START:
8536 MERGE_SOURCE_PATH:MERGED_RANGE->END is a valid description of
8537 the merge -- see normalize_merge_sources() and the global comment
8538 'MERGEINFO MERGE SOURCE NORMALIZATION'.
8540 However, if there *was* a copy, then
8541 MERGE_SOURCE_PATH:MERGED_RANGE->START doesn't exist or is
8542 unrelated to MERGE_SOURCE_PATH:MERGED_RANGE->END. Also, we
8543 don't know if (MERGE_SOURCE_PATH:MERGED_RANGE->START)+1 through
8544 (MERGE_SOURCE_PATH:MERGED_RANGE->END)-1 actually exist.
8546 If CHILD is a subtree of the merge target, then nothing is
8547 guaranteed beyond the fact that MERGE_SOURCE_PATH exists at
8548 MERGED_RANGE->END. */
8549 if ((!merge_b->record_only || merge_b->reintegrate_merge)
8553 svn_mergeinfo_t subtree_history_as_mergeinfo;
8554 svn_rangelist_t *child_merge_src_rangelist;
8555 svn_client__pathrev_t *subtree_mergeinfo_pathrev
8556 = svn_client__pathrev_create_with_relpath(
8557 merge_b->target->loc.repos_root_url,
8558 merge_b->target->loc.repos_uuid,
8559 merged_range->end, child_merge_src_fspath + 1,
8562 /* Confirm that the naive mergeinfo we want to set on
8563 CHILD->ABSPATH both exists and is part of
8564 (MERGE_SOURCE_PATH+CHILD_REPOS_PATH)@MERGED_RANGE->END's
8566 /* We know MERGED_RANGE->END is younger than MERGE_RANGE->START
8567 because we only do this for forward merges. */
8568 err = svn_client__get_history_as_mergeinfo(
8569 &subtree_history_as_mergeinfo, NULL,
8570 subtree_mergeinfo_pathrev,
8571 merged_range->end, merged_range->start,
8572 merge_b->ra_session2, merge_b->ctx, iterpool);
8574 /* If CHILD is a subtree it may have been deleted prior to
8575 MERGED_RANGE->END so the above call to get its history
8579 if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
8580 return svn_error_trace(err);
8581 svn_error_clear(err);
8585 child_merge_src_rangelist = svn_hash_gets(
8586 subtree_history_as_mergeinfo,
8587 child_merge_src_fspath);
8588 SVN_ERR(svn_rangelist_intersect(&child_merge_rangelist,
8589 child_merge_rangelist,
8590 child_merge_src_rangelist,
8592 if (child->record_noninheritable)
8593 svn_rangelist__set_inheritance(child_merge_rangelist,
8598 svn_hash_sets(child_merges, child->abspath, child_merge_rangelist);
8599 SVN_ERR(update_wc_mergeinfo(result_catalog,
8601 child_merge_src_fspath,
8602 child_merges, is_rollback,
8603 merge_b->ctx, iterpool));
8605 /* Once is enough: We don't need to record mergeinfo describing
8606 the merge a second. If CHILD->ABSPATH is in
8607 MERGE_B->ADDED_ABSPATHS, we'll do just that, so remove the
8608 former from the latter. */
8609 svn_hash_sets(merge_b->added_abspaths, child->abspath, NULL);
8612 /* Elide explicit subtree mergeinfo whether or not we updated it. */
8615 svn_boolean_t in_switched_subtree = FALSE;
8617 if (child->switched)
8618 in_switched_subtree = TRUE;
8621 /* Check if CHILD is part of a switched subtree */
8622 svn_client__merge_path_t *parent;
8626 parent = APR_ARRAY_IDX(children_with_mergeinfo,
8627 j, svn_client__merge_path_t *);
8630 && svn_dirent_is_ancestor(parent->abspath,
8633 in_switched_subtree = TRUE;
8639 /* Allow mergeinfo on switched subtrees to elide to the
8640 repository. Otherwise limit elision to the merge target
8641 for now. do_directory_merge() will eventually try to
8642 elide that when the merge is complete. */
8643 SVN_ERR(svn_client__elide_mergeinfo(
8645 in_switched_subtree ? NULL : merge_b->target->abspath,
8646 merge_b->ctx, iterpool));
8648 } /* (i = 0; i < notify_b->children_with_mergeinfo->nelts; i++) */
8650 svn_pool_destroy(iterpool);
8651 return SVN_NO_ERROR;
8654 /* Helper for do_directory_merge().
8656 Record mergeinfo describing a merge of
8657 MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
8658 MERGEINFO_FSPATH to each path in ADDED_ABSPATHS which has explicit
8659 mergeinfo or is the immediate child of a parent with explicit
8660 non-inheritable mergeinfo.
8662 DEPTH, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS, are
8663 cascaded from do_directory_merge's arguments of the same names.
8665 Note: This is intended to support forward merges only, i.e.
8666 MERGED_RANGE->START must be older than MERGED_RANGE->END.
8668 static svn_error_t *
8669 record_mergeinfo_for_added_subtrees(
8670 svn_merge_range_t *merged_range,
8671 const char *mergeinfo_fspath,
8673 svn_boolean_t squelch_mergeinfo_notifications,
8674 apr_hash_t *added_abspaths,
8675 merge_cmd_baton_t *merge_b,
8678 apr_pool_t *iterpool;
8679 apr_hash_index_t *hi;
8681 /* If no paths were added by the merge then we have nothing to do. */
8682 if (!added_abspaths)
8683 return SVN_NO_ERROR;
8685 SVN_ERR_ASSERT(merged_range->start < merged_range->end);
8687 iterpool = svn_pool_create(pool);
8688 for (hi = apr_hash_first(pool, added_abspaths); hi; hi = apr_hash_next(hi))
8690 const char *added_abspath = svn__apr_hash_index_key(hi);
8691 const char *dir_abspath;
8692 svn_mergeinfo_t parent_mergeinfo;
8693 svn_mergeinfo_t added_path_mergeinfo;
8695 svn_pool_clear(iterpool);
8696 dir_abspath = svn_dirent_dirname(added_abspath, iterpool);
8698 /* Grab the added path's explicit mergeinfo. */
8699 SVN_ERR(svn_client__get_wc_mergeinfo(&added_path_mergeinfo, NULL,
8700 svn_mergeinfo_explicit,
8701 added_abspath, NULL, NULL, FALSE,
8702 merge_b->ctx, iterpool, iterpool));
8704 /* If the added path doesn't have explicit mergeinfo, does its immediate
8705 parent have non-inheritable mergeinfo? */
8706 if (!added_path_mergeinfo)
8707 SVN_ERR(svn_client__get_wc_mergeinfo(&parent_mergeinfo, NULL,
8708 svn_mergeinfo_explicit,
8709 dir_abspath, NULL, NULL, FALSE,
8711 iterpool, iterpool));
8713 if (added_path_mergeinfo
8714 || svn_mergeinfo__is_noninheritable(parent_mergeinfo, iterpool))
8716 svn_node_kind_t added_path_kind;
8717 svn_mergeinfo_t merge_mergeinfo;
8718 svn_mergeinfo_t adds_history_as_mergeinfo;
8719 svn_rangelist_t *rangelist;
8720 const char *rel_added_path;
8721 const char *added_path_mergeinfo_fspath;
8722 svn_client__pathrev_t *added_path_pathrev;
8724 SVN_ERR(svn_wc_read_kind2(&added_path_kind, merge_b->ctx->wc_ctx,
8725 added_abspath, FALSE, FALSE, iterpool));
8727 /* Calculate the naive mergeinfo describing the merge. */
8728 merge_mergeinfo = apr_hash_make(iterpool);
8729 rangelist = svn_rangelist__initialize(
8730 merged_range->start, merged_range->end,
8731 ((added_path_kind == svn_node_file)
8732 || (!(depth == svn_depth_infinity
8733 || depth == svn_depth_immediates))),
8736 /* Create the new mergeinfo path for added_path's mergeinfo.
8737 (added_abspath had better be a child of MERGE_B->target->abspath
8738 or something is *really* wrong.) */
8739 rel_added_path = svn_dirent_is_child(merge_b->target->abspath,
8740 added_abspath, iterpool);
8741 SVN_ERR_ASSERT(rel_added_path);
8742 added_path_mergeinfo_fspath = svn_fspath__join(mergeinfo_fspath,
8745 svn_hash_sets(merge_mergeinfo, added_path_mergeinfo_fspath,
8748 /* Don't add new mergeinfo to describe the merge if that mergeinfo
8749 contains non-existent merge sources.
8751 We know that MERGEINFO_PATH/rel_added_path's history does not
8752 span MERGED_RANGE->START:MERGED_RANGE->END but rather that it
8753 was added at some revions greater than MERGED_RANGE->START
8754 (assuming this is a forward merge). It may have been added,
8755 deleted, and re-added many times. The point is that we cannot
8756 blindly apply the naive mergeinfo calculated above because it
8757 will describe non-existent merge sources. To avoid this we get
8758 take the intersection of the naive mergeinfo with
8759 MERGEINFO_PATH/rel_added_path's history. */
8760 added_path_pathrev = svn_client__pathrev_create_with_relpath(
8761 merge_b->target->loc.repos_root_url,
8762 merge_b->target->loc.repos_uuid,
8763 MAX(merged_range->start, merged_range->end),
8764 added_path_mergeinfo_fspath + 1, iterpool);
8765 SVN_ERR(svn_client__get_history_as_mergeinfo(
8766 &adds_history_as_mergeinfo, NULL,
8768 MAX(merged_range->start, merged_range->end),
8769 MIN(merged_range->start, merged_range->end),
8770 merge_b->ra_session2, merge_b->ctx, iterpool));
8772 SVN_ERR(svn_mergeinfo_intersect2(&merge_mergeinfo,
8774 adds_history_as_mergeinfo,
8775 FALSE, iterpool, iterpool));
8777 /* Combine the explicit mergeinfo on the added path (if any)
8778 with the mergeinfo describing this merge. */
8779 if (added_path_mergeinfo)
8780 SVN_ERR(svn_mergeinfo_merge2(merge_mergeinfo,
8781 added_path_mergeinfo,
8782 iterpool, iterpool));
8783 SVN_ERR(svn_client__record_wc_mergeinfo(
8784 added_abspath, merge_mergeinfo,
8785 !squelch_mergeinfo_notifications, merge_b->ctx, iterpool));
8788 svn_pool_destroy(iterpool);
8790 return SVN_NO_ERROR;
8792 /* Baton structure for log_noop_revs. */
8793 typedef struct log_noop_baton_t
8795 /* See the comment 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start
8797 apr_array_header_t *children_with_mergeinfo;
8799 /* Absolute repository path of younger of the two merge sources
8801 const char *source_fspath;
8803 /* The merge target. */
8804 const merge_target_t *target;
8806 /* Initially empty rangelists allocated in POOL. The rangelists are
8807 * populated across multiple invocations of log_noop_revs(). */
8808 svn_rangelist_t *operative_ranges;
8809 svn_rangelist_t *merged_ranges;
8811 /* Pool to store the rangelists. */
8815 /* Helper for log_noop_revs: Merge a svn_merge_range_t representation of
8816 REVISION into RANGELIST. New elements added to rangelist are allocated
8819 This is *not* a general purpose rangelist merge but a special replacement
8820 for svn_rangelist_merge when REVISION is guaranteed to be younger than any
8821 element in RANGELIST. svn_rangelist_merge is O(n) worst-case (i.e. when
8822 all the ranges in output rangelist are older than the incoming changes).
8823 This turns the special case of a single incoming younger range into O(1).
8825 static svn_error_t *
8826 rangelist_merge_revision(svn_rangelist_t *rangelist,
8827 svn_revnum_t revision,
8828 apr_pool_t *result_pool)
8830 svn_merge_range_t *new_range;
8831 if (rangelist->nelts)
8833 svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
8834 svn_merge_range_t *);
8835 if (range->end == revision - 1)
8837 /* REVISION is adjacent to the youngest range in RANGELIST
8838 so we can simply expand that range to encompass REVISION. */
8839 range->end = revision;
8840 return SVN_NO_ERROR;
8843 new_range = apr_palloc(result_pool, sizeof(*new_range));
8844 new_range->start = revision - 1;
8845 new_range->end = revision;
8846 new_range->inheritable = TRUE;
8848 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = new_range;
8850 return SVN_NO_ERROR;
8853 /* Implements the svn_log_entry_receiver_t interface.
8855 BATON is an log_noop_baton_t *.
8857 Add LOG_ENTRY->REVISION to BATON->OPERATIVE_RANGES.
8859 If LOG_ENTRY->REVISION has already been fully merged to
8860 BATON->target->abspath per the mergeinfo in BATON->CHILDREN_WITH_MERGEINFO,
8861 then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES.
8863 Use SCRATCH_POOL for temporary allocations. Allocate additions to
8864 BATON->MERGED_RANGES and BATON->OPERATIVE_RANGES in BATON->POOL.
8866 Note: This callback must be invoked from oldest LOG_ENTRY->REVISION
8867 to youngest LOG_ENTRY->REVISION -- see rangelist_merge_revision().
8869 static svn_error_t *
8870 log_noop_revs(void *baton,
8871 svn_log_entry_t *log_entry,
8872 apr_pool_t *scratch_pool)
8874 log_noop_baton_t *log_gap_baton = baton;
8875 apr_hash_index_t *hi;
8876 svn_revnum_t revision;
8877 svn_boolean_t log_entry_rev_required = FALSE;
8879 revision = log_entry->revision;
8881 /* It's possible that authz restrictions on the merge source prevent us
8882 from knowing about any of the changes for LOG_ENTRY->REVISION. */
8883 if (!log_entry->changed_paths2)
8884 return SVN_NO_ERROR;
8886 /* Unconditionally add LOG_ENTRY->REVISION to BATON->OPERATIVE_MERGES. */
8887 SVN_ERR(rangelist_merge_revision(log_gap_baton->operative_ranges,
8889 log_gap_baton->pool));
8891 /* Examine each path affected by LOG_ENTRY->REVISION. If the explicit or
8892 inherited mergeinfo for *all* of the corresponding paths under
8893 BATON->target->abspath reflects that LOG_ENTRY->REVISION has been
8894 merged, then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES. */
8895 for (hi = apr_hash_first(scratch_pool, log_entry->changed_paths2);
8897 hi = apr_hash_next(hi))
8899 const char *fspath = svn__apr_hash_index_key(hi);
8900 const char *rel_path;
8901 const char *cwmi_abspath;
8902 svn_rangelist_t *paths_explicit_rangelist = NULL;
8903 svn_boolean_t mergeinfo_inherited = FALSE;
8905 /* Adjust REL_PATH so it is relative to the merge source then use it to
8906 calculate what path in the merge target would be affected by this
8908 rel_path = svn_fspath__skip_ancestor(log_gap_baton->source_fspath,
8910 /* Is PATH even within the merge target? If it isn't we
8911 can disregard it altogether. */
8912 if (rel_path == NULL)
8914 cwmi_abspath = svn_dirent_join(log_gap_baton->target->abspath,
8915 rel_path, scratch_pool);
8917 /* Find any explicit or inherited mergeinfo for PATH. */
8918 while (!log_entry_rev_required)
8920 svn_client__merge_path_t *child = get_child_with_mergeinfo(
8921 log_gap_baton->children_with_mergeinfo, cwmi_abspath);
8923 if (child && child->pre_merge_mergeinfo)
8925 /* Found some explicit mergeinfo, grab any ranges
8927 paths_explicit_rangelist =
8928 svn_hash_gets(child->pre_merge_mergeinfo, fspath);
8932 if (cwmi_abspath[0] == '\0'
8933 || svn_dirent_is_root(cwmi_abspath, strlen(cwmi_abspath))
8934 || strcmp(log_gap_baton->target->abspath, cwmi_abspath) == 0)
8936 /* Can't crawl any higher. */
8940 /* Didn't find anything so crawl up to the parent. */
8941 cwmi_abspath = svn_dirent_dirname(cwmi_abspath, scratch_pool);
8942 fspath = svn_fspath__dirname(fspath, scratch_pool);
8944 /* At this point *if* we find mergeinfo it will be inherited. */
8945 mergeinfo_inherited = TRUE;
8948 if (paths_explicit_rangelist)
8950 svn_rangelist_t *intersecting_range;
8951 svn_rangelist_t *rangelist;
8953 rangelist = svn_rangelist__initialize(revision - 1, revision, TRUE,
8956 /* If PATH inherited mergeinfo we must consider inheritance in the
8957 event the inherited mergeinfo is actually non-inheritable. */
8958 SVN_ERR(svn_rangelist_intersect(&intersecting_range,
8959 paths_explicit_rangelist,
8961 mergeinfo_inherited, scratch_pool));
8963 if (intersecting_range->nelts == 0)
8964 log_entry_rev_required = TRUE;
8968 log_entry_rev_required = TRUE;
8972 if (!log_entry_rev_required)
8973 SVN_ERR(rangelist_merge_revision(log_gap_baton->merged_ranges,
8975 log_gap_baton->pool));
8977 return SVN_NO_ERROR;
8980 /* Helper for do_directory_merge().
8982 SOURCE is cascaded from the argument of the same name in
8983 do_directory_merge(). TARGET is the merge target. RA_SESSION is the
8984 session for SOURCE->loc2.
8986 Find all the ranges required by subtrees in
8987 CHILDREN_WITH_MERGEINFO that are *not* required by
8988 TARGET->abspath (i.e. CHILDREN_WITH_MERGEINFO[0]). If such
8989 ranges exist, then find any subset of ranges which, if merged, would be
8990 inoperative. Finally, if any inoperative ranges are found then remove
8991 these ranges from all of the subtree's REMAINING_RANGES.
8993 This function should only be called when honoring mergeinfo during
8994 forward merges (i.e. SOURCE->rev1 < SOURCE->rev2).
8996 static svn_error_t *
8997 remove_noop_subtree_ranges(const merge_source_t *source,
8998 const merge_target_t *target,
8999 svn_ra_session_t *ra_session,
9000 apr_array_header_t *children_with_mergeinfo,
9001 apr_pool_t *result_pool,
9002 apr_pool_t *scratch_pool)
9004 /* ### Do we need to check that we are at a uniform working revision? */
9006 svn_client__merge_path_t *root_child =
9007 APR_ARRAY_IDX(children_with_mergeinfo, 0, svn_client__merge_path_t *);
9008 svn_rangelist_t *requested_ranges;
9009 svn_rangelist_t *subtree_gap_ranges;
9010 svn_rangelist_t *subtree_remaining_ranges;
9011 log_noop_baton_t log_gap_baton;
9012 svn_merge_range_t *oldest_gap_rev;
9013 svn_merge_range_t *youngest_gap_rev;
9014 svn_rangelist_t *inoperative_ranges;
9015 apr_pool_t *iterpool;
9016 const char *longest_common_subtree_ancestor = NULL;
9019 assert(session_url_is(ra_session, source->loc2->url, scratch_pool));
9021 /* This function is only intended to work with forward merges. */
9022 if (source->loc1->rev > source->loc2->rev)
9023 return SVN_NO_ERROR;
9025 /* Another easy out: There are no subtrees. */
9026 if (children_with_mergeinfo->nelts < 2)
9027 return SVN_NO_ERROR;
9029 subtree_remaining_ranges = apr_array_make(scratch_pool, 1,
9030 sizeof(svn_merge_range_t *));
9032 /* Given the requested merge of SOURCE->rev1:rev2 might there be any
9033 part of this range required for subtrees but not for the target? */
9034 requested_ranges = svn_rangelist__initialize(MIN(source->loc1->rev,
9036 MAX(source->loc1->rev,
9038 TRUE, scratch_pool);
9039 SVN_ERR(svn_rangelist_remove(&subtree_gap_ranges,
9040 root_child->remaining_ranges,
9041 requested_ranges, FALSE, scratch_pool));
9043 /* Early out, nothing to operate on */
9044 if (!subtree_gap_ranges->nelts)
9045 return SVN_NO_ERROR;
9047 /* Create a rangelist describing every range required across all subtrees. */
9048 iterpool = svn_pool_create(scratch_pool);
9049 for (i = 1; i < children_with_mergeinfo->nelts; i++)
9051 svn_client__merge_path_t *child =
9052 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
9054 svn_pool_clear(iterpool);
9056 /* Issue #4269: Keep track of the longest common ancestor of all the
9057 subtrees which require merges. This may be a child of
9058 TARGET->ABSPATH, which will allow us to narrow the log request
9060 if (child->remaining_ranges && child->remaining_ranges->nelts)
9062 if (longest_common_subtree_ancestor)
9063 longest_common_subtree_ancestor = svn_dirent_get_longest_ancestor(
9064 longest_common_subtree_ancestor, child->abspath, scratch_pool);
9066 longest_common_subtree_ancestor = child->abspath;
9069 /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
9070 if (child->remaining_ranges && child->remaining_ranges->nelts)
9071 SVN_ERR(svn_rangelist_merge2(subtree_remaining_ranges,
9072 child->remaining_ranges,
9073 scratch_pool, iterpool));
9075 svn_pool_destroy(iterpool);
9077 /* It's possible that none of the subtrees had any remaining ranges. */
9078 if (!subtree_remaining_ranges->nelts)
9079 return SVN_NO_ERROR;
9081 /* Ok, *finally* we can answer what part(s) of SOURCE->rev1:rev2 are
9082 required for the subtrees but not the target. */
9083 SVN_ERR(svn_rangelist_intersect(&subtree_gap_ranges,
9085 subtree_remaining_ranges, FALSE,
9088 /* Another early out */
9089 if (!subtree_gap_ranges->nelts)
9090 return SVN_NO_ERROR;
9092 /* One or more subtrees need some revisions that the target doesn't need.
9093 Use log to determine if any of these revisions are inoperative. */
9094 oldest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges, 0, svn_merge_range_t *);
9095 youngest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges,
9096 subtree_gap_ranges->nelts - 1, svn_merge_range_t *);
9098 /* Set up the log baton. */
9099 log_gap_baton.children_with_mergeinfo = children_with_mergeinfo;
9100 log_gap_baton.source_fspath
9101 = svn_client__pathrev_fspath(source->loc2, result_pool);
9102 log_gap_baton.target = target;
9103 log_gap_baton.merged_ranges = apr_array_make(scratch_pool, 0,
9104 sizeof(svn_revnum_t *));
9105 log_gap_baton.operative_ranges = apr_array_make(scratch_pool, 0,
9106 sizeof(svn_revnum_t *));
9107 log_gap_baton.pool = svn_pool_create(scratch_pool);
9109 /* Find the longest common ancestor of all subtrees relative to
9110 RA_SESSION's URL. */
9111 if (longest_common_subtree_ancestor)
9112 longest_common_subtree_ancestor =
9113 svn_dirent_skip_ancestor(target->abspath,
9114 longest_common_subtree_ancestor);
9116 longest_common_subtree_ancestor = "";
9118 /* Invoke the svn_log_entry_receiver_t receiver log_noop_revs() from
9119 oldest to youngest. The receiver is optimized to add ranges to
9120 log_gap_baton.merged_ranges and log_gap_baton.operative_ranges, but
9121 requires that the revs arrive oldest to youngest -- see log_noop_revs()
9122 and rangelist_merge_revision(). */
9123 err = get_log(ra_session, longest_common_subtree_ancestor,
9124 oldest_gap_rev->start + 1, youngest_gap_rev->end, TRUE,
9125 log_noop_revs, &log_gap_baton, scratch_pool);
9127 /* It's possible that the only subtrees with mergeinfo in TARGET don't have
9128 any corresponding subtree in SOURCE between SOURCE->REV1 < SOURCE->REV2.
9129 So it's also possible that we may ask for the logs of non-existent paths.
9130 If we do, then assume that no subtree requires any ranges that are not
9131 already required by the TARGET. */
9134 if (err->apr_err != SVN_ERR_FS_NOT_FOUND
9135 && longest_common_subtree_ancestor[0] != '\0')
9136 return svn_error_trace(err);
9138 /* Asked about a non-existent subtree in SOURCE. */
9139 svn_error_clear(err);
9140 log_gap_baton.merged_ranges =
9141 svn_rangelist__initialize(oldest_gap_rev->start,
9142 youngest_gap_rev->end,
9143 TRUE, scratch_pool);
9147 inoperative_ranges = svn_rangelist__initialize(oldest_gap_rev->start,
9148 youngest_gap_rev->end,
9149 TRUE, scratch_pool);
9150 SVN_ERR(svn_rangelist_remove(&(inoperative_ranges),
9151 log_gap_baton.operative_ranges,
9152 inoperative_ranges, FALSE, scratch_pool));
9153 SVN_ERR(svn_rangelist_merge2(log_gap_baton.merged_ranges, inoperative_ranges,
9154 scratch_pool, scratch_pool));
9157 for (i = 1; i < children_with_mergeinfo->nelts; i++)
9159 svn_client__merge_path_t *child =
9160 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
9162 /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
9163 if (child->remaining_ranges && child->remaining_ranges->nelts)
9165 /* Remove inoperative ranges from all children so we don't perform
9166 inoperative editor drives. */
9167 SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
9168 log_gap_baton.merged_ranges,
9169 child->remaining_ranges,
9170 FALSE, result_pool));
9174 svn_pool_destroy(log_gap_baton.pool);
9176 return SVN_NO_ERROR;
9179 /* Perform a merge of changes in SOURCE to the working copy path
9180 TARGET_ABSPATH. Both URLs in SOURCE, and TARGET_ABSPATH all represent
9181 directories -- for the single file case, the caller should use
9184 CHILDREN_WITH_MERGEINFO and MERGE_B describe the merge being performed
9185 As this function is for a mergeinfo-aware merge, SOURCE->ancestral
9186 should be TRUE, and SOURCE->loc1 must be a historical ancestor of
9187 SOURCE->loc2, or vice-versa (see `MERGEINFO MERGE SOURCE NORMALIZATION'
9188 for more requirements around SOURCE).
9190 Mergeinfo changes will be recorded unless MERGE_B->dry_run is true.
9192 If mergeinfo is being recorded, SQUELCH_MERGEINFO_NOTIFICATIONS is FALSE,
9193 and MERGE_B->CTX->NOTIFY_FUNC2 is not NULL, then call
9194 MERGE_B->CTX->NOTIFY_FUNC2 with MERGE_B->CTX->NOTIFY_BATON2 and a
9195 svn_wc_notify_merge_record_info_begin notification before any mergeinfo
9196 changes are made to describe the merge performed.
9198 If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
9199 is not NULL, then don't record the new mergeinfo on the WC, but instead
9200 record it in RESULT_CATALOG, where the keys are absolute working copy
9201 paths and the values are the new mergeinfos for each. Allocate additions
9202 to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
9204 Handle DEPTH as documented for svn_client_merge5().
9206 CONFLICT_REPORT is as documented for do_directory_merge().
9208 Perform any temporary allocations in SCRATCH_POOL.
9210 NOTE: This is a wrapper around drive_merge_report_editor() which
9211 handles the complexities inherent to situations where a given
9212 directory's children may have intersecting merges (because they
9213 meet one or more of the criteria described in get_mergeinfo_paths()).
9215 static svn_error_t *
9216 do_mergeinfo_aware_dir_merge(svn_mergeinfo_catalog_t result_catalog,
9217 single_range_conflict_report_t **conflict_report,
9218 const merge_source_t *source,
9219 const char *target_abspath,
9220 apr_array_header_t *children_with_mergeinfo,
9221 const svn_diff_tree_processor_t *processor,
9223 svn_boolean_t squelch_mergeinfo_notifications,
9224 merge_cmd_baton_t *merge_b,
9225 apr_pool_t *result_pool,
9226 apr_pool_t *scratch_pool)
9228 /* The range defining the mergeinfo we will record to describe the merge
9229 (assuming we are recording mergeinfo
9231 Note: This may be a subset of SOURCE->rev1:rev2 if
9232 populate_remaining_ranges() determines that some part of
9233 SOURCE->rev1:rev2 has already been wholly merged to TARGET_ABSPATH.
9234 Also, the actual editor drive(s) may be a subset of RANGE, if
9235 remove_noop_subtree_ranges() and/or fix_deleted_subtree_ranges()
9236 further tweak things. */
9237 svn_merge_range_t range;
9239 svn_ra_session_t *ra_session;
9240 svn_client__merge_path_t *target_merge_path;
9241 svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
9243 SVN_ERR_ASSERT(source->ancestral);
9245 /*** If we get here, we're dealing with related sources from the
9246 same repository as the target -- merge tracking might be
9249 *conflict_report = NULL;
9251 /* Point our RA_SESSION to the URL of our youngest merge source side. */
9252 ra_session = is_rollback ? merge_b->ra_session1 : merge_b->ra_session2;
9254 /* Fill NOTIFY_B->CHILDREN_WITH_MERGEINFO with child paths (const
9255 svn_client__merge_path_t *) which might have intersecting merges
9256 because they meet one or more of the criteria described in
9257 get_mergeinfo_paths(). Here the paths are arranged in a depth
9259 SVN_ERR(get_mergeinfo_paths(children_with_mergeinfo,
9260 merge_b->target, depth,
9261 merge_b->dry_run, merge_b->same_repos,
9262 merge_b->ctx, scratch_pool, scratch_pool));
9264 /* The first item from the NOTIFY_B->CHILDREN_WITH_MERGEINFO is always
9265 the target thanks to depth-first ordering. */
9266 target_merge_path = APR_ARRAY_IDX(children_with_mergeinfo, 0,
9267 svn_client__merge_path_t *);
9269 /* If we are honoring mergeinfo, then for each item in
9270 NOTIFY_B->CHILDREN_WITH_MERGEINFO, we need to calculate what needs to be
9271 merged, and then merge it. Otherwise, we just merge what we were asked
9272 to merge across the whole tree. */
9273 SVN_ERR(populate_remaining_ranges(children_with_mergeinfo,
9275 merge_b, scratch_pool, scratch_pool));
9277 /* Always start with a range which describes the most inclusive merge
9278 possible, i.e. SOURCE->rev1:rev2. */
9279 range.start = source->loc1->rev;
9280 range.end = source->loc2->rev;
9281 range.inheritable = TRUE;
9283 if (!merge_b->reintegrate_merge)
9285 svn_revnum_t new_range_start, start_rev;
9286 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
9288 /* The merge target TARGET_ABSPATH and/or its subtrees may not need all
9289 of SOURCE->rev1:rev2 applied. So examine
9290 NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the oldest starting
9291 revision that actually needs to be merged (for reverse merges this is
9292 the youngest starting revision).
9294 We'll do this twice, right now for the start of the mergeinfo we will
9295 ultimately record to describe this merge and then later for the
9296 start of the actual editor drive. */
9297 new_range_start = get_most_inclusive_rev(
9298 children_with_mergeinfo, is_rollback, TRUE);
9299 if (SVN_IS_VALID_REVNUM(new_range_start))
9300 range.start = new_range_start;
9302 /* Remove inoperative ranges from any subtrees' remaining_ranges
9303 to spare the expense of noop editor drives. */
9305 SVN_ERR(remove_noop_subtree_ranges(source, merge_b->target,
9307 children_with_mergeinfo,
9308 scratch_pool, iterpool));
9310 /* Adjust subtrees' remaining_ranges to deal with issue #3067:
9311 * "subtrees that don't exist at the start or end of a merge range
9312 * shouldn't break the merge". */
9313 SVN_ERR(fix_deleted_subtree_ranges(source, merge_b->target,
9315 children_with_mergeinfo,
9316 merge_b->ctx, scratch_pool, iterpool));
9318 /* remove_noop_subtree_ranges() and/or fix_deleted_subtree_range()
9319 may have further refined the starting revision for our editor
9322 get_most_inclusive_rev(children_with_mergeinfo,
9325 /* Is there anything to merge? */
9326 if (SVN_IS_VALID_REVNUM(start_rev))
9328 /* Now examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the oldest
9329 ending revision that actually needs to be merged (for reverse
9330 merges this is the youngest ending revision). */
9331 svn_revnum_t end_rev =
9332 get_most_inclusive_rev(children_with_mergeinfo,
9333 is_rollback, FALSE);
9335 /* While END_REV is valid, do the following:
9337 1. Tweak each NOTIFY_B->CHILDREN_WITH_MERGEINFO element so that
9338 the element's remaining_ranges member has as its first element
9339 a range that ends with end_rev.
9341 2. Starting with start_rev, call drive_merge_report_editor()
9342 on MERGE_B->target->abspath for start_rev:end_rev.
9344 3. Remove the first element from each
9345 NOTIFY_B->CHILDREN_WITH_MERGEINFO element's remaining_ranges
9348 4. Again examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the most
9349 inclusive starting revision that actually needs to be merged and
9350 update start_rev. This prevents us from needlessly contacting the
9351 repository and doing a diff where we describe the entire target
9352 tree as *not* needing any of the requested range. This can happen
9353 whenever we have mergeinfo with gaps in it for the merge source.
9355 5. Again examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the most
9356 inclusive ending revision that actually needs to be merged and
9359 6. Lather, rinse, repeat.
9362 while (end_rev != SVN_INVALID_REVNUM)
9364 merge_source_t *real_source;
9365 svn_merge_range_t *first_target_range
9366 = (target_merge_path->remaining_ranges->nelts == 0 ? NULL
9367 : APR_ARRAY_IDX(target_merge_path->remaining_ranges, 0,
9368 svn_merge_range_t *));
9370 /* Issue #3324: Stop editor abuse! Don't call
9371 drive_merge_report_editor() in such a way that we request an
9372 editor with svn_client__get_diff_editor() for some rev X,
9373 then call svn_ra_do_diff3() for some revision Y, and then
9374 call reporter->set_path(PATH=="") to set the root revision
9375 for the editor drive to revision Z where
9376 (X != Z && X < Z < Y). This is bogus because the server will
9377 send us the diff between X:Y but the client is expecting the
9378 diff between Y:Z. See issue #3324 for full details on the
9379 problems this can cause. */
9380 if (first_target_range
9381 && start_rev != first_target_range->start)
9385 if (end_rev < first_target_range->start)
9386 end_rev = first_target_range->start;
9390 if (end_rev > first_target_range->start)
9391 end_rev = first_target_range->start;
9395 svn_pool_clear(iterpool);
9397 slice_remaining_ranges(children_with_mergeinfo,
9398 is_rollback, end_rev, scratch_pool);
9400 /* Reset variables that must be reset for every drive */
9401 merge_b->notify_begin.last_abspath = NULL;
9403 real_source = subrange_source(source, start_rev, end_rev, iterpool);
9404 SVN_ERR(drive_merge_report_editor(
9405 merge_b->target->abspath,
9407 children_with_mergeinfo,
9413 /* If any paths picked up explicit mergeinfo as a result of
9414 the merge we need to make sure any mergeinfo those paths
9415 inherited is recorded and then add these paths to
9416 NOTIFY_B->CHILDREN_WITH_MERGEINFO.*/
9417 SVN_ERR(process_children_with_new_mergeinfo(
9418 merge_b, children_with_mergeinfo,
9421 /* If any subtrees had their explicit mergeinfo deleted as a
9422 result of the merge then remove these paths from
9423 NOTIFY_B->CHILDREN_WITH_MERGEINFO since there is no need
9424 to consider these subtrees for subsequent editor drives
9425 nor do we want to record mergeinfo on them describing
9426 the merge itself. */
9427 remove_children_with_deleted_mergeinfo(
9428 merge_b, children_with_mergeinfo);
9430 /* Prepare for the next iteration (if any). */
9431 remove_first_range_from_remaining_ranges(
9432 end_rev, children_with_mergeinfo, scratch_pool);
9434 /* If we raised any conflicts, break out and report how much
9436 if (is_path_conflicted_by_merge(merge_b))
9438 merge_source_t *remaining_range = NULL;
9440 if (real_source->loc2->rev != source->loc2->rev)
9441 remaining_range = subrange_source(source,
9442 real_source->loc2->rev,
9445 *conflict_report = single_range_conflict_report_create(
9446 real_source, remaining_range,
9449 range.end = end_rev;
9454 get_most_inclusive_rev(children_with_mergeinfo,
9457 get_most_inclusive_rev(children_with_mergeinfo,
9458 is_rollback, FALSE);
9461 svn_pool_destroy(iterpool);
9465 if (!merge_b->record_only)
9467 /* Reset cur_ancestor_abspath to null so that subsequent cherry
9468 picked revision ranges will be notified upon subsequent
9470 merge_b->notify_begin.last_abspath = NULL;
9472 SVN_ERR(drive_merge_report_editor(merge_b->target->abspath,
9482 /* Record mergeinfo where appropriate.*/
9483 if (RECORD_MERGEINFO(merge_b))
9485 const svn_client__pathrev_t *primary_src
9486 = is_rollback ? source->loc1 : source->loc2;
9487 const char *mergeinfo_path
9488 = svn_client__pathrev_fspath(primary_src, scratch_pool);
9490 SVN_ERR(record_mergeinfo_for_dir_merge(result_catalog,
9493 children_with_mergeinfo,
9495 squelch_mergeinfo_notifications,
9499 /* If a path has an immediate parent with non-inheritable mergeinfo at
9500 this point, then it meets criteria 3 or 5 described in
9501 get_mergeinfo_paths' doc string. For paths which exist prior to a
9502 merge explicit mergeinfo has already been set. But for paths added
9503 during the merge this is not the case. The path might have explicit
9504 mergeinfo from the merge source, but no mergeinfo yet exists
9505 describing *this* merge. So the added path has either incomplete
9506 explicit mergeinfo or inherits incomplete mergeinfo from its
9507 immediate parent (if any, the parent might have only non-inheritable
9508 ranges in which case the path simply inherits empty mergeinfo).
9510 So here we look at the root path of each subtree added during the
9511 merge and set explicit mergeinfo on it if it meets the aforementioned
9513 if (range.start < range.end) /* Nothing to record on added subtrees
9514 resulting from reverse merges. */
9516 SVN_ERR(record_mergeinfo_for_added_subtrees(
9517 &range, mergeinfo_path, depth,
9518 squelch_mergeinfo_notifications,
9519 merge_b->added_abspaths, merge_b, scratch_pool));
9523 return SVN_NO_ERROR;
9526 /* Helper for do_merge() when the merge target is a directory.
9528 * If any conflict is raised during the merge, set *CONFLICTED_RANGE to
9529 * the revision sub-range that raised the conflict. In this case, the
9530 * merge will have ended at revision CONFLICTED_RANGE and mergeinfo will
9531 * have been recorded for all revision sub-ranges up to and including
9532 * CONFLICTED_RANGE. Otherwise, set *CONFLICTED_RANGE to NULL.
9534 static svn_error_t *
9535 do_directory_merge(svn_mergeinfo_catalog_t result_catalog,
9536 single_range_conflict_report_t **conflict_report,
9537 const merge_source_t *source,
9538 const char *target_abspath,
9539 const svn_diff_tree_processor_t *processor,
9541 svn_boolean_t squelch_mergeinfo_notifications,
9542 merge_cmd_baton_t *merge_b,
9543 apr_pool_t *result_pool,
9544 apr_pool_t *scratch_pool)
9546 apr_array_header_t *children_with_mergeinfo;
9548 /* Initialize CHILDREN_WITH_MERGEINFO. See the comment
9549 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */
9550 children_with_mergeinfo =
9551 apr_array_make(scratch_pool, 16, sizeof(svn_client__merge_path_t *));
9553 /* And make it read-only accessible from the baton */
9554 merge_b->notify_begin.nodes_with_mergeinfo = children_with_mergeinfo;
9556 /* If we are not honoring mergeinfo we can skip right to the
9557 business of merging changes! */
9558 if (HONOR_MERGEINFO(merge_b))
9559 SVN_ERR(do_mergeinfo_aware_dir_merge(result_catalog, conflict_report,
9560 source, target_abspath,
9561 children_with_mergeinfo,
9563 squelch_mergeinfo_notifications,
9564 merge_b, result_pool, scratch_pool));
9566 SVN_ERR(do_mergeinfo_unaware_dir_merge(conflict_report,
9567 source, target_abspath,
9568 children_with_mergeinfo,
9570 merge_b, result_pool, scratch_pool));
9572 merge_b->notify_begin.nodes_with_mergeinfo = NULL;
9574 return SVN_NO_ERROR;
9577 /** Ensure that *RA_SESSION is opened to URL, either by reusing
9578 * *RA_SESSION if it is non-null and already opened to URL's
9579 * repository, or by allocating a new *RA_SESSION in POOL.
9580 * (RA_SESSION itself cannot be null, of course.)
9582 * CTX is used as for svn_client_open_ra_session().
9584 static svn_error_t *
9585 ensure_ra_session_url(svn_ra_session_t **ra_session,
9587 const char *wri_abspath,
9588 svn_client_ctx_t *ctx,
9591 svn_error_t *err = SVN_NO_ERROR;
9595 err = svn_ra_reparent(*ra_session, url, pool);
9598 /* SVN_ERR_RA_ILLEGAL_URL is raised when url doesn't point to the same
9599 repository as ra_session. */
9600 if (! *ra_session || (err && err->apr_err == SVN_ERR_RA_ILLEGAL_URL))
9602 svn_error_clear(err);
9603 err = svn_client_open_ra_session2(ra_session, url, wri_abspath,
9608 return SVN_NO_ERROR;
9611 /* Drive a merge of MERGE_SOURCES into working copy node TARGET
9612 and possibly record mergeinfo describing the merge -- see
9615 If MODIFIED_SUBTREES is not NULL and all the MERGE_SOURCES are 'ancestral'
9616 or REINTEGRATE_MERGE is true, then replace *MODIFIED_SUBTREES with a new
9617 hash containing all the paths that *MODIFIED_SUBTREES contained before,
9618 and also every path modified, skipped, added, or tree-conflicted
9619 by the merge. Keys and values of the hash are both (const char *)
9620 absolute paths. The contents of the hash are allocated in RESULT_POOL.
9622 If the merge raises any conflicts while merging a revision range, return
9623 early and set *CONFLICT_REPORT to describe the details. (In this case,
9624 notify that the merge is complete if and only if this was the last
9625 revision range of the merge.) If there are no conflicts, set
9626 *CONFLICT_REPORT to NULL. A revision range here can be one specified
9627 in MERGE_SOURCES or an internally generated sub-range of one of those
9628 when merge tracking is in use.
9630 For every (const merge_source_t *) merge source in MERGE_SOURCES, if
9631 SOURCE->ANCESTRAL is set, then the "left" and "right" side are
9632 ancestrally related. (See 'MERGEINFO MERGE SOURCE NORMALIZATION'
9633 for more on what that means and how it matters.)
9635 If SOURCES_RELATED is set, the "left" and "right" sides of the
9636 merge source are historically related (ancestors, uncles, second
9637 cousins thrice removed, etc...). (This is passed through to
9638 do_file_merge() to simulate the history checks that the repository
9639 logic does in the directory case.)
9641 SAME_REPOS is TRUE iff the merge sources live in the same
9642 repository as the one from which the target working copy has been
9645 If mergeinfo is being recorded, SQUELCH_MERGEINFO_NOTIFICATIONS is FALSE,
9646 and CTX->NOTIFY_FUNC2 is not NULL, then call CTX->NOTIFY_FUNC2 with
9647 CTX->NOTIFY_BATON2 and a svn_wc_notify_merge_record_info_begin
9648 notification before any mergeinfo changes are made to describe the merge
9651 If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
9652 is not NULL, then don't record the new mergeinfo on the WC, but instead
9653 record it in RESULT_CATALOG, where the keys are absolute working copy
9654 paths and the values are the new mergeinfos for each. Allocate additions
9655 to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
9657 FORCE_DELETE, DRY_RUN, RECORD_ONLY, DEPTH, MERGE_OPTIONS,
9658 and CTX are as described in the docstring for svn_client_merge_peg3().
9660 If IGNORE_MERGEINFO is true, disable merge tracking, by treating the two
9661 sources as unrelated even if they actually have a common ancestor. See
9662 the macro HONOR_MERGEINFO().
9664 If DIFF_IGNORE_ANCESTRY is true, diff the 'left' and 'right' versions
9665 of a node (if they are the same kind) as if they were related, even if
9666 they are not related. Otherwise, diff unrelated items as a deletion
9667 of one thing and the addition of another.
9669 If not NULL, RECORD_ONLY_PATHS is a hash of (const char *) paths mapped
9670 to the same. If RECORD_ONLY is true and RECORD_ONLY_PATHS is not NULL,
9671 then record mergeinfo describing the merge only on subtrees which contain
9672 items from RECORD_ONLY_PATHS. If RECORD_ONLY is true and RECORD_ONLY_PATHS
9673 is NULL, then record mergeinfo on every subtree with mergeinfo in
9676 REINTEGRATE_MERGE is TRUE if this is a reintegrate merge.
9678 *USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
9679 integrity, *USE_SLEEP will be unchanged if no sleep is required.
9681 SCRATCH_POOL is used for all temporary allocations.
9683 static svn_error_t *
9684 do_merge(apr_hash_t **modified_subtrees,
9685 svn_mergeinfo_catalog_t result_catalog,
9686 conflict_report_t **conflict_report,
9687 svn_boolean_t *use_sleep,
9688 const apr_array_header_t *merge_sources,
9689 const merge_target_t *target,
9690 svn_ra_session_t *src_session,
9691 svn_boolean_t sources_related,
9692 svn_boolean_t same_repos,
9693 svn_boolean_t ignore_mergeinfo,
9694 svn_boolean_t diff_ignore_ancestry,
9695 svn_boolean_t force_delete,
9696 svn_boolean_t dry_run,
9697 svn_boolean_t record_only,
9698 apr_hash_t *record_only_paths,
9699 svn_boolean_t reintegrate_merge,
9700 svn_boolean_t squelch_mergeinfo_notifications,
9702 const apr_array_header_t *merge_options,
9703 svn_client_ctx_t *ctx,
9704 apr_pool_t *result_pool,
9705 apr_pool_t *scratch_pool)
9707 merge_cmd_baton_t merge_cmd_baton = { 0 };
9709 const char *diff3_cmd;
9710 const char *preserved_exts_str;
9712 svn_boolean_t checked_mergeinfo_capability = FALSE;
9713 svn_ra_session_t *ra_session1 = NULL, *ra_session2 = NULL;
9714 const char *old_src_session_url = NULL;
9715 apr_pool_t *iterpool;
9716 const svn_diff_tree_processor_t *processor;
9718 SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath));
9720 *conflict_report = NULL;
9722 /* Check from some special conditions when in record-only mode
9723 (which is a merge-tracking thing). */
9726 svn_boolean_t sources_ancestral = TRUE;
9729 /* Find out whether all of the sources are 'ancestral'. */
9730 for (j = 0; j < merge_sources->nelts; j++)
9731 if (! APR_ARRAY_IDX(merge_sources, j, merge_source_t *)->ancestral)
9733 sources_ancestral = FALSE;
9737 /* We can't do a record-only merge if the sources aren't related. */
9738 if (! sources_ancestral)
9739 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
9740 _("Use of two URLs is not compatible with "
9741 "mergeinfo modification"));
9743 /* We can't do a record-only merge if the sources aren't from
9744 the same repository as the target. */
9746 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
9747 _("Merge from foreign repository is not "
9748 "compatible with mergeinfo modification"));
9750 /* If this is a dry-run record-only merge, there's nothing to do. */
9752 return SVN_NO_ERROR;
9755 iterpool = svn_pool_create(scratch_pool);
9757 /* Ensure a known depth. */
9758 if (depth == svn_depth_unknown)
9759 depth = svn_depth_infinity;
9761 /* Set up the diff3 command, so various callers don't have to. */
9763 ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG)
9765 svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS,
9766 SVN_CONFIG_OPTION_DIFF3_CMD, NULL);
9768 if (diff3_cmd != NULL)
9769 SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool));
9771 /* See which files the user wants to preserve the extension of when
9772 conflict files are made. */
9773 svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY,
9774 SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, "");
9776 /* Build the merge context baton (or at least the parts of it that
9777 don't need to be reset for each merge source). */
9778 merge_cmd_baton.force_delete = force_delete;
9779 merge_cmd_baton.dry_run = dry_run;
9780 merge_cmd_baton.record_only = record_only;
9781 merge_cmd_baton.ignore_mergeinfo = ignore_mergeinfo;
9782 merge_cmd_baton.diff_ignore_ancestry = diff_ignore_ancestry;
9783 merge_cmd_baton.same_repos = same_repos;
9784 merge_cmd_baton.mergeinfo_capable = FALSE;
9785 merge_cmd_baton.ctx = ctx;
9786 merge_cmd_baton.reintegrate_merge = reintegrate_merge;
9787 merge_cmd_baton.target = target;
9788 merge_cmd_baton.pool = iterpool;
9789 merge_cmd_baton.merge_options = merge_options;
9790 merge_cmd_baton.diff3_cmd = diff3_cmd;
9791 merge_cmd_baton.ext_patterns = *preserved_exts_str
9792 ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ",
9793 FALSE, scratch_pool)
9796 merge_cmd_baton.use_sleep = use_sleep;
9798 /* Do we already know the specific subtrees with mergeinfo we want
9799 to record-only mergeinfo on? */
9800 if (record_only && record_only_paths)
9801 merge_cmd_baton.merged_abspaths = record_only_paths;
9803 merge_cmd_baton.merged_abspaths = apr_hash_make(result_pool);
9805 merge_cmd_baton.skipped_abspaths = apr_hash_make(result_pool);
9806 merge_cmd_baton.added_abspaths = apr_hash_make(result_pool);
9807 merge_cmd_baton.tree_conflicted_abspaths = apr_hash_make(result_pool);
9810 svn_diff_tree_processor_t *merge_processor;
9812 merge_processor = svn_diff__tree_processor_create(&merge_cmd_baton,
9815 merge_processor->dir_opened = merge_dir_opened;
9816 merge_processor->dir_changed = merge_dir_changed;
9817 merge_processor->dir_added = merge_dir_added;
9818 merge_processor->dir_deleted = merge_dir_deleted;
9819 merge_processor->dir_closed = merge_dir_closed;
9821 merge_processor->file_opened = merge_file_opened;
9822 merge_processor->file_changed = merge_file_changed;
9823 merge_processor->file_added = merge_file_added;
9824 merge_processor->file_deleted = merge_file_deleted;
9825 /* Not interested in file_closed() */
9827 merge_processor->node_absent = merge_node_absent;
9829 processor = merge_processor;
9834 SVN_ERR(svn_ra_get_session_url(src_session, &old_src_session_url,
9836 ra_session1 = src_session;
9839 for (i = 0; i < merge_sources->nelts; i++)
9841 svn_node_kind_t src1_kind;
9842 merge_source_t *source =
9843 APR_ARRAY_IDX(merge_sources, i, merge_source_t *);
9844 single_range_conflict_report_t *conflicted_range_report;
9846 svn_pool_clear(iterpool);
9848 /* Sanity check: if our left- and right-side merge sources are
9849 the same, there's nothing to here. */
9850 if ((strcmp(source->loc1->url, source->loc2->url) == 0)
9851 && (source->loc1->rev == source->loc2->rev))
9854 /* Establish RA sessions to our URLs, reuse where possible. */
9855 SVN_ERR(ensure_ra_session_url(&ra_session1, source->loc1->url,
9856 target->abspath, ctx, scratch_pool));
9857 SVN_ERR(ensure_ra_session_url(&ra_session2, source->loc2->url,
9858 target->abspath, ctx, scratch_pool));
9860 /* Populate the portions of the merge context baton that need to
9861 be reset for each merge source iteration. */
9862 merge_cmd_baton.merge_source = *source;
9863 merge_cmd_baton.implicit_src_gap = NULL;
9864 merge_cmd_baton.conflicted_paths = NULL;
9865 merge_cmd_baton.paths_with_new_mergeinfo = NULL;
9866 merge_cmd_baton.paths_with_deleted_mergeinfo = NULL;
9867 merge_cmd_baton.ra_session1 = ra_session1;
9868 merge_cmd_baton.ra_session2 = ra_session2;
9870 merge_cmd_baton.notify_begin.last_abspath = NULL;
9872 /* Populate the portions of the merge context baton that require
9873 an RA session to set, but shouldn't be reset for each iteration. */
9874 if (! checked_mergeinfo_capability)
9876 SVN_ERR(svn_ra_has_capability(ra_session1,
9877 &merge_cmd_baton.mergeinfo_capable,
9878 SVN_RA_CAPABILITY_MERGEINFO,
9880 checked_mergeinfo_capability = TRUE;
9883 SVN_ERR(svn_ra_check_path(ra_session1, "", source->loc1->rev,
9884 &src1_kind, iterpool));
9886 /* Run the merge; if there are conflicts, allow the callback to
9887 * resolve them, and if it resolves all of them, then run the
9888 * merge again with the remaining revision range, until it is all
9892 /* Merge as far as possible without resolving any conflicts */
9893 if (src1_kind != svn_node_dir)
9895 SVN_ERR(do_file_merge(result_catalog, &conflicted_range_report,
9896 source, target->abspath,
9899 squelch_mergeinfo_notifications,
9900 &merge_cmd_baton, iterpool, iterpool));
9902 else /* Directory */
9904 SVN_ERR(do_directory_merge(result_catalog, &conflicted_range_report,
9905 source, target->abspath,
9907 depth, squelch_mergeinfo_notifications,
9908 &merge_cmd_baton, iterpool, iterpool));
9911 /* Give the conflict resolver callback the opportunity to
9912 * resolve any conflicts that were raised. If it resolves all
9913 * of them, go around again to merge the next sub-range (if any). */
9914 if (conflicted_range_report && ctx->conflict_func2 && ! dry_run)
9916 svn_boolean_t conflicts_remain;
9918 SVN_ERR(svn_client__resolve_conflicts(
9919 &conflicts_remain, merge_cmd_baton.conflicted_paths,
9921 if (conflicts_remain)
9924 merge_cmd_baton.conflicted_paths = NULL;
9925 /* Caution: this source is in iterpool */
9926 source = conflicted_range_report->remaining_source;
9927 conflicted_range_report = NULL;
9934 /* The final mergeinfo on TARGET_WCPATH may itself elide. */
9936 SVN_ERR(svn_client__elide_mergeinfo(target->abspath, NULL,
9939 /* If conflicts occurred while merging any but the very last
9940 * range of a multi-pass merge, we raise an error that aborts
9941 * the merge. The user will be asked to resolve conflicts
9942 * before merging subsequent revision ranges. */
9943 if (conflicted_range_report)
9945 *conflict_report = conflict_report_create(
9946 target->abspath, conflicted_range_report->conflicted_range,
9947 (i == merge_sources->nelts - 1
9948 && ! conflicted_range_report->remaining_source),
9954 if (! *conflict_report || (*conflict_report)->was_last_range)
9956 /* Let everyone know we're finished here. */
9957 notify_merge_completed(target->abspath, ctx, iterpool);
9960 /* Does the caller want to know what the merge has done? */
9961 if (modified_subtrees)
9963 *modified_subtrees =
9964 apr_hash_overlay(result_pool, *modified_subtrees,
9965 merge_cmd_baton.merged_abspaths);
9966 *modified_subtrees =
9967 apr_hash_overlay(result_pool, *modified_subtrees,
9968 merge_cmd_baton.added_abspaths);
9969 *modified_subtrees =
9970 apr_hash_overlay(result_pool, *modified_subtrees,
9971 merge_cmd_baton.skipped_abspaths);
9972 *modified_subtrees =
9973 apr_hash_overlay(result_pool, *modified_subtrees,
9974 merge_cmd_baton.tree_conflicted_abspaths);
9978 SVN_ERR(svn_ra_reparent(src_session, old_src_session_url, iterpool));
9980 svn_pool_destroy(iterpool);
9981 return SVN_NO_ERROR;
9984 /* Perform a two-URL merge between URLs which are related, but neither
9985 is a direct ancestor of the other. This first does a real two-URL
9986 merge (unless this is record-only), followed by record-only merges
9987 to represent the changed mergeinfo.
9989 Set *CONFLICT_REPORT to indicate if there were any conflicts, as in
9992 The diff to be merged is between SOURCE->loc1 (in URL1_RA_SESSION1)
9993 and SOURCE->loc2 (in URL2_RA_SESSION2); YCA is their youngest
9996 SAME_REPOS must be true if and only if the source URLs are in the same
9997 repository as the target working copy.
9999 DIFF_IGNORE_ANCESTRY is as in do_merge().
10001 Other arguments are as in all of the public merge APIs.
10003 *USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
10004 integrity, *USE_SLEEP will be unchanged if no sleep is required.
10006 SCRATCH_POOL is used for all temporary allocations.
10008 static svn_error_t *
10009 merge_cousins_and_supplement_mergeinfo(conflict_report_t **conflict_report,
10010 svn_boolean_t *use_sleep,
10011 const merge_target_t *target,
10012 svn_ra_session_t *URL1_ra_session,
10013 svn_ra_session_t *URL2_ra_session,
10014 const merge_source_t *source,
10015 const svn_client__pathrev_t *yca,
10016 svn_boolean_t same_repos,
10018 svn_boolean_t diff_ignore_ancestry,
10019 svn_boolean_t force_delete,
10020 svn_boolean_t record_only,
10021 svn_boolean_t dry_run,
10022 const apr_array_header_t *merge_options,
10023 svn_client_ctx_t *ctx,
10024 apr_pool_t *result_pool,
10025 apr_pool_t *scratch_pool)
10027 apr_array_header_t *remove_sources, *add_sources;
10028 apr_hash_t *modified_subtrees = NULL;
10030 /* Sure we could use SCRATCH_POOL throughout this function, but since this
10031 is a wrapper around three separate merges we'll create a subpool we can
10032 clear between each of the three. If the merge target has a lot of
10033 subtree mergeinfo, then this will help keep memory use in check. */
10034 apr_pool_t *subpool = svn_pool_create(scratch_pool);
10036 assert(session_url_is(URL1_ra_session, source->loc1->url, scratch_pool));
10037 assert(session_url_is(URL2_ra_session, source->loc2->url, scratch_pool));
10039 SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath));
10040 SVN_ERR_ASSERT(! source->ancestral);
10042 SVN_ERR(normalize_merge_sources_internal(
10043 &remove_sources, source->loc1,
10044 svn_rangelist__initialize(source->loc1->rev, yca->rev, TRUE,
10046 URL1_ra_session, ctx, scratch_pool, subpool));
10048 SVN_ERR(normalize_merge_sources_internal(
10049 &add_sources, source->loc2,
10050 svn_rangelist__initialize(yca->rev, source->loc2->rev, TRUE,
10052 URL2_ra_session, ctx, scratch_pool, subpool));
10054 *conflict_report = NULL;
10056 /* If this isn't a record-only merge, we'll first do a stupid
10057 point-to-point merge... */
10060 apr_array_header_t *faux_sources =
10061 apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
10063 modified_subtrees = apr_hash_make(scratch_pool);
10064 APR_ARRAY_PUSH(faux_sources, const merge_source_t *) = source;
10065 SVN_ERR(do_merge(&modified_subtrees, NULL, conflict_report, use_sleep,
10066 faux_sources, target,
10067 URL1_ra_session, TRUE, same_repos,
10068 FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10069 force_delete, dry_run, FALSE, NULL, TRUE,
10070 FALSE, depth, merge_options, ctx,
10071 scratch_pool, subpool));
10072 if (*conflict_report)
10074 *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10075 if (! (*conflict_report)->was_last_range)
10076 return SVN_NO_ERROR;
10079 else if (! same_repos)
10081 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
10082 _("Merge from foreign repository is not "
10083 "compatible with mergeinfo modification"));
10086 /* ... and now, if we're doing the mergeinfo thang, we execute a
10087 pair of record-only merges using the real sources we've
10090 Issue #3648: We don't actually perform these two record-only merges
10091 on the WC at first, but rather see what each would do and store that
10092 in two mergeinfo catalogs. We then merge the catalogs together and
10093 then record the result in the WC. This prevents the second record
10094 only merge from removing legitimate mergeinfo history, from the same
10095 source, that was made in prior merges. */
10096 if (same_repos && !dry_run)
10098 svn_mergeinfo_catalog_t add_result_catalog =
10099 apr_hash_make(scratch_pool);
10100 svn_mergeinfo_catalog_t remove_result_catalog =
10101 apr_hash_make(scratch_pool);
10103 notify_mergeinfo_recording(target->abspath, NULL, ctx, scratch_pool);
10104 svn_pool_clear(subpool);
10105 SVN_ERR(do_merge(NULL, add_result_catalog, conflict_report, use_sleep,
10106 add_sources, target,
10107 URL1_ra_session, TRUE, same_repos,
10108 FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10109 force_delete, dry_run, TRUE,
10110 modified_subtrees, TRUE,
10111 TRUE, depth, merge_options, ctx,
10112 scratch_pool, subpool));
10113 if (*conflict_report)
10115 *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10116 if (! (*conflict_report)->was_last_range)
10117 return SVN_NO_ERROR;
10119 svn_pool_clear(subpool);
10120 SVN_ERR(do_merge(NULL, remove_result_catalog, conflict_report, use_sleep,
10121 remove_sources, target,
10122 URL1_ra_session, TRUE, same_repos,
10123 FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10124 force_delete, dry_run, TRUE,
10125 modified_subtrees, TRUE,
10126 TRUE, depth, merge_options, ctx,
10127 scratch_pool, subpool));
10128 if (*conflict_report)
10130 *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10131 if (! (*conflict_report)->was_last_range)
10132 return SVN_NO_ERROR;
10134 SVN_ERR(svn_mergeinfo_catalog_merge(add_result_catalog,
10135 remove_result_catalog,
10136 scratch_pool, scratch_pool));
10137 SVN_ERR(svn_client__record_wc_mergeinfo_catalog(add_result_catalog,
10138 ctx, scratch_pool));
10141 svn_pool_destroy(subpool);
10142 return SVN_NO_ERROR;
10145 /* Perform checks to determine whether the working copy at TARGET_ABSPATH
10146 * can safely be used as a merge target. Checks are performed according to
10147 * the ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, and ALLOW_SWITCHED_SUBTREES
10148 * parameters. If any checks fail, raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE.
10150 * E.g. if all the ALLOW_* parameters are FALSE, TARGET_ABSPATH must
10151 * be a single-revision, pristine, unswitched working copy.
10152 * In other words, it must reflect a subtree of the repository as found
10153 * at single revision -- although sparse checkouts are permitted. */
10154 static svn_error_t *
10155 ensure_wc_is_suitable_merge_target(const char *target_abspath,
10156 svn_client_ctx_t *ctx,
10157 svn_boolean_t allow_mixed_rev,
10158 svn_boolean_t allow_local_mods,
10159 svn_boolean_t allow_switched_subtrees,
10160 apr_pool_t *scratch_pool)
10162 svn_node_kind_t target_kind;
10164 /* Check the target exists. */
10165 SVN_ERR(svn_io_check_path(target_abspath, &target_kind, scratch_pool));
10166 if (target_kind == svn_node_none)
10167 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
10168 _("Path '%s' does not exist"),
10169 svn_dirent_local_style(target_abspath,
10171 SVN_ERR(svn_wc_read_kind2(&target_kind, ctx->wc_ctx, target_abspath,
10172 FALSE, FALSE, scratch_pool));
10173 if (target_kind != svn_node_dir && target_kind != svn_node_file)
10174 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
10175 _("Merge target '%s' does not exist in the "
10176 "working copy"), target_abspath);
10178 /* Perform the mixed-revision check first because it's the cheapest one. */
10179 if (! allow_mixed_rev)
10181 svn_revnum_t min_rev;
10182 svn_revnum_t max_rev;
10184 SVN_ERR(svn_client_min_max_revisions(&min_rev, &max_rev, target_abspath,
10185 FALSE, ctx, scratch_pool));
10187 if (!(SVN_IS_VALID_REVNUM(min_rev) && SVN_IS_VALID_REVNUM(max_rev)))
10189 svn_boolean_t is_added;
10191 /* Allow merge into added nodes. */
10192 SVN_ERR(svn_wc__node_is_added(&is_added, ctx->wc_ctx, target_abspath,
10195 return SVN_NO_ERROR;
10197 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10198 _("Cannot determine revision of working "
10202 if (min_rev != max_rev)
10203 return svn_error_createf(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
10204 _("Cannot merge into mixed-revision working "
10205 "copy [%ld:%ld]; try updating first"),
10209 /* Next, check for switched subtrees. */
10210 if (! allow_switched_subtrees)
10212 svn_boolean_t is_switched;
10214 SVN_ERR(svn_wc__has_switched_subtrees(&is_switched, ctx->wc_ctx,
10215 target_abspath, NULL,
10218 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10219 _("Cannot merge into a working copy "
10220 "with a switched subtree"));
10223 /* This is the most expensive check, so it is performed last.*/
10224 if (! allow_local_mods)
10226 svn_boolean_t is_modified;
10228 SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx,
10234 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10235 _("Cannot merge into a working copy "
10236 "that has local modifications"));
10239 return SVN_NO_ERROR;
10242 /* Throw an error if PATH_OR_URL is a path and REVISION isn't a repository
10244 static svn_error_t *
10245 ensure_wc_path_has_repo_revision(const char *path_or_url,
10246 const svn_opt_revision_t *revision,
10247 apr_pool_t *scratch_pool)
10249 if (revision->kind != svn_opt_revision_number
10250 && revision->kind != svn_opt_revision_date
10251 && revision->kind != svn_opt_revision_head
10252 && ! svn_path_is_url(path_or_url))
10253 return svn_error_createf(
10254 SVN_ERR_CLIENT_BAD_REVISION, NULL,
10255 _("Invalid merge source '%s'; a working copy path can only be "
10256 "used with a repository revision (a number, a date, or head)"),
10257 svn_dirent_local_style(path_or_url, scratch_pool));
10258 return SVN_NO_ERROR;
10261 /* "Open" the target WC for a merge. That means:
10262 * - find out its exact repository location
10263 * - check the WC for suitability (throw an error if unsuitable)
10265 * Set *TARGET_P to a new, fully initialized, target description structure.
10267 * ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, ALLOW_SWITCHED_SUBTREES determine
10268 * whether the WC is deemed suitable; see ensure_wc_is_suitable_merge_target()
10271 * If the node is locally added, the rev and URL will be null/invalid. Some
10272 * kinds of merge can use such a target; others can't.
10274 static svn_error_t *
10275 open_target_wc(merge_target_t **target_p,
10276 const char *wc_abspath,
10277 svn_boolean_t allow_mixed_rev,
10278 svn_boolean_t allow_local_mods,
10279 svn_boolean_t allow_switched_subtrees,
10280 svn_client_ctx_t *ctx,
10281 apr_pool_t *result_pool,
10282 apr_pool_t *scratch_pool)
10284 merge_target_t *target = apr_palloc(result_pool, sizeof(*target));
10285 svn_client__pathrev_t *origin;
10287 target->abspath = apr_pstrdup(result_pool, wc_abspath);
10289 SVN_ERR(svn_client__wc_node_get_origin(&origin, wc_abspath, ctx,
10290 result_pool, scratch_pool));
10293 target->loc = *origin;
10298 /* The node has no location in the repository. It's unversioned or
10299 * locally added or locally deleted.
10301 * If it's locally added or deleted, find the repository root
10302 * URL and UUID anyway, and leave the node URL and revision as NULL
10303 * and INVALID. If it's unversioned, this will throw an error. */
10304 err = svn_wc__node_get_repos_info(NULL, NULL,
10305 &target->loc.repos_root_url,
10306 &target->loc.repos_uuid,
10307 ctx->wc_ctx, wc_abspath,
10308 result_pool, scratch_pool);
10312 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
10313 && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY
10314 && err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
10315 return svn_error_trace(err);
10317 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, err,
10318 _("Merge target '%s' does not exist in the "
10320 svn_dirent_local_style(wc_abspath,
10324 target->loc.rev = SVN_INVALID_REVNUM;
10325 target->loc.url = NULL;
10328 SVN_ERR(ensure_wc_is_suitable_merge_target(
10330 allow_mixed_rev, allow_local_mods, allow_switched_subtrees,
10333 *target_p = target;
10334 return SVN_NO_ERROR;
10337 /*-----------------------------------------------------------------------*/
10339 /*** Public APIs ***/
10341 /* The body of svn_client_merge5(), which see for details.
10343 * If SOURCE1 @ REVISION1 is related to SOURCE2 @ REVISION2 then use merge
10344 * tracking (subject to other constraints -- see HONOR_MERGEINFO());
10345 * otherwise disable merge tracking.
10347 * IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge().
10349 static svn_error_t *
10350 merge_locked(conflict_report_t **conflict_report,
10351 const char *source1,
10352 const svn_opt_revision_t *revision1,
10353 const char *source2,
10354 const svn_opt_revision_t *revision2,
10355 const char *target_abspath,
10357 svn_boolean_t ignore_mergeinfo,
10358 svn_boolean_t diff_ignore_ancestry,
10359 svn_boolean_t force_delete,
10360 svn_boolean_t record_only,
10361 svn_boolean_t dry_run,
10362 svn_boolean_t allow_mixed_rev,
10363 const apr_array_header_t *merge_options,
10364 svn_client_ctx_t *ctx,
10365 apr_pool_t *result_pool,
10366 apr_pool_t *scratch_pool)
10368 merge_target_t *target;
10369 svn_client__pathrev_t *source1_loc, *source2_loc;
10370 svn_boolean_t sources_related = FALSE;
10371 svn_ra_session_t *ra_session1, *ra_session2;
10372 apr_array_header_t *merge_sources;
10374 svn_boolean_t use_sleep = FALSE;
10375 svn_client__pathrev_t *yca = NULL;
10376 apr_pool_t *sesspool;
10377 svn_boolean_t same_repos;
10379 /* ### FIXME: This function really ought to do a history check on
10380 the left and right sides of the merge source, and -- if one is an
10381 ancestor of the other -- just call svn_client_merge_peg3() with
10382 the appropriate args. */
10384 SVN_ERR(open_target_wc(&target, target_abspath,
10385 allow_mixed_rev, TRUE, TRUE,
10386 ctx, scratch_pool, scratch_pool));
10388 /* Open RA sessions to both sides of our merge source, and resolve URLs
10389 * and revisions. */
10390 sesspool = svn_pool_create(scratch_pool);
10391 SVN_ERR(svn_client__ra_session_from_path2(
10392 &ra_session1, &source1_loc,
10393 source1, NULL, revision1, revision1, ctx, sesspool));
10394 SVN_ERR(svn_client__ra_session_from_path2(
10395 &ra_session2, &source2_loc,
10396 source2, NULL, revision2, revision2, ctx, sesspool));
10398 /* We can't do a diff between different repositories. */
10399 /* ### We should also insist that the root URLs of the two sources match,
10400 * as we are only carrying around a single source-repos-root from now
10401 * on, and URL calculations will go wrong if they differ.
10402 * Alternatively, teach the code to cope with differing root URLs. */
10403 SVN_ERR(check_same_repos(source1_loc, source1_loc->url,
10404 source2_loc, source2_loc->url,
10405 FALSE /* strict_urls */, scratch_pool));
10407 /* Do our working copy and sources come from the same repository? */
10408 same_repos = is_same_repos(&target->loc, source1_loc, TRUE /* strict_urls */);
10410 /* Unless we're ignoring ancestry, see if the two sources are related. */
10411 if (! ignore_mergeinfo)
10412 SVN_ERR(svn_client__get_youngest_common_ancestor(
10413 &yca, source1_loc, source2_loc, ra_session1, ctx,
10414 scratch_pool, scratch_pool));
10416 /* Check for a youngest common ancestor. If we have one, we'll be
10417 doing merge tracking.
10419 So, given a requested merge of the differences between A and
10420 B, and a common ancestor of C, we will find ourselves in one of
10421 four positions, and four different approaches:
10423 A == B == C there's nothing to merge
10425 A == C != B we merge the changes between A (or C) and B
10427 B == C != A we merge the changes between B (or C) and A
10429 A != B != C we merge the changes between A and B without
10430 merge recording, then record-only two merges:
10431 from A to C, and from C to B
10435 /* Note that our merge sources are related. */
10436 sources_related = TRUE;
10438 /* If the common ancestor matches the right side of our merge,
10439 then we only need to reverse-merge the left side. */
10440 if ((strcmp(yca->url, source2_loc->url) == 0)
10441 && (yca->rev == source2_loc->rev))
10443 SVN_ERR(normalize_merge_sources_internal(
10444 &merge_sources, source1_loc,
10445 svn_rangelist__initialize(source1_loc->rev, yca->rev, TRUE,
10447 ra_session1, ctx, scratch_pool, scratch_pool));
10449 /* If the common ancestor matches the left side of our merge,
10450 then we only need to merge the right side. */
10451 else if ((strcmp(yca->url, source1_loc->url) == 0)
10452 && (yca->rev == source1_loc->rev))
10454 SVN_ERR(normalize_merge_sources_internal(
10455 &merge_sources, source2_loc,
10456 svn_rangelist__initialize(yca->rev, source2_loc->rev, TRUE,
10458 ra_session2, ctx, scratch_pool, scratch_pool));
10460 /* And otherwise, we need to do both: reverse merge the left
10461 side, and merge the right. */
10464 merge_source_t source;
10466 source.loc1 = source1_loc;
10467 source.loc2 = source2_loc;
10468 source.ancestral = FALSE;
10470 err = merge_cousins_and_supplement_mergeinfo(conflict_report,
10479 diff_ignore_ancestry,
10481 record_only, dry_run,
10486 /* Close our temporary RA sessions (this could've happened
10487 after the second call to normalize_merge_sources() inside
10488 the merge_cousins_and_supplement_mergeinfo() routine). */
10489 svn_pool_destroy(sesspool);
10492 svn_io_sleep_for_timestamps(target->abspath, scratch_pool);
10495 return SVN_NO_ERROR;
10500 /* Build a single-item merge_source_t array. */
10501 merge_sources = apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
10502 APR_ARRAY_PUSH(merge_sources, merge_source_t *)
10503 = merge_source_create(source1_loc, source2_loc, FALSE, scratch_pool);
10506 err = do_merge(NULL, NULL, conflict_report, &use_sleep,
10507 merge_sources, target,
10508 ra_session1, sources_related, same_repos,
10509 ignore_mergeinfo, diff_ignore_ancestry, force_delete, dry_run,
10510 record_only, NULL, FALSE, FALSE, depth, merge_options,
10511 ctx, result_pool, scratch_pool);
10513 /* Close our temporary RA sessions. */
10514 svn_pool_destroy(sesspool);
10517 svn_io_sleep_for_timestamps(target->abspath, scratch_pool);
10520 return SVN_NO_ERROR;
10523 /* Set *TARGET_ABSPATH to the absolute path of, and *LOCK_ABSPATH to
10524 the absolute path to lock for, TARGET_WCPATH. */
10525 static svn_error_t *
10526 get_target_and_lock_abspath(const char **target_abspath,
10527 const char **lock_abspath,
10528 const char *target_wcpath,
10529 svn_client_ctx_t *ctx,
10530 apr_pool_t *result_pool)
10532 svn_node_kind_t kind;
10533 SVN_ERR(svn_dirent_get_absolute(target_abspath, target_wcpath,
10535 SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, *target_abspath,
10536 FALSE, FALSE, result_pool));
10537 if (kind == svn_node_dir)
10538 *lock_abspath = *target_abspath;
10540 *lock_abspath = svn_dirent_dirname(*target_abspath, result_pool);
10542 return SVN_NO_ERROR;
10546 svn_client_merge5(const char *source1,
10547 const svn_opt_revision_t *revision1,
10548 const char *source2,
10549 const svn_opt_revision_t *revision2,
10550 const char *target_wcpath,
10552 svn_boolean_t ignore_mergeinfo,
10553 svn_boolean_t diff_ignore_ancestry,
10554 svn_boolean_t force_delete,
10555 svn_boolean_t record_only,
10556 svn_boolean_t dry_run,
10557 svn_boolean_t allow_mixed_rev,
10558 const apr_array_header_t *merge_options,
10559 svn_client_ctx_t *ctx,
10562 const char *target_abspath, *lock_abspath;
10563 conflict_report_t *conflict_report;
10565 /* Sanity check our input -- we require specified revisions,
10566 * and either 2 paths or 2 URLs. */
10567 if ((revision1->kind == svn_opt_revision_unspecified)
10568 || (revision2->kind == svn_opt_revision_unspecified))
10569 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
10570 _("Not all required revisions are specified"));
10571 if (svn_path_is_url(source1) != svn_path_is_url(source2))
10572 return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
10573 _("Merge sources must both be "
10574 "either paths or URLs"));
10575 /* A WC path must be used with a repository revision, as we can't
10576 * (currently) use the WC itself as a source, we can only read the URL
10577 * from it and use that. */
10578 SVN_ERR(ensure_wc_path_has_repo_revision(source1, revision1, pool));
10579 SVN_ERR(ensure_wc_path_has_repo_revision(source2, revision2, pool));
10581 SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
10582 target_wcpath, ctx, pool));
10585 SVN_WC__CALL_WITH_WRITE_LOCK(
10586 merge_locked(&conflict_report,
10587 source1, revision1, source2, revision2,
10588 target_abspath, depth, ignore_mergeinfo,
10589 diff_ignore_ancestry,
10590 force_delete, record_only, dry_run,
10591 allow_mixed_rev, merge_options, ctx, pool, pool),
10592 ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
10594 SVN_ERR(merge_locked(&conflict_report,
10595 source1, revision1, source2, revision2,
10596 target_abspath, depth, ignore_mergeinfo,
10597 diff_ignore_ancestry,
10598 force_delete, record_only, dry_run,
10599 allow_mixed_rev, merge_options, ctx, pool, pool));
10601 SVN_ERR(make_merge_conflict_error(conflict_report, pool));
10602 return SVN_NO_ERROR;
10606 /* Check if mergeinfo for a given path is described explicitly or via
10607 inheritance in a mergeinfo catalog.
10609 If REPOS_REL_PATH exists in CATALOG and has mergeinfo containing
10610 MERGEINFO, then set *IN_CATALOG to TRUE. If REPOS_REL_PATH does
10611 not exist in CATALOG, then find its nearest parent which does exist.
10612 If the mergeinfo REPOS_REL_PATH would inherit from that parent
10613 contains MERGEINFO then set *IN_CATALOG to TRUE. Set *IN_CATALOG
10614 to FALSE in all other cases.
10616 Set *CAT_KEY_PATH to the key path in CATALOG for REPOS_REL_PATH's
10617 explicit or inherited mergeinfo. If no explicit or inherited mergeinfo
10618 is found for REPOS_REL_PATH then set *CAT_KEY_PATH to NULL.
10620 User RESULT_POOL to allocate *CAT_KEY_PATH. Use SCRATCH_POOL for
10621 temporary allocations. */
10622 static svn_error_t *
10623 mergeinfo_in_catalog(svn_boolean_t *in_catalog,
10624 const char **cat_key_path,
10625 const char *repos_rel_path,
10626 svn_mergeinfo_t mergeinfo,
10627 svn_mergeinfo_catalog_t catalog,
10628 apr_pool_t *result_pool,
10629 apr_pool_t *scratch_pool)
10631 const char *walk_path = NULL;
10633 *in_catalog = FALSE;
10634 *cat_key_path = NULL;
10636 if (mergeinfo && catalog && apr_hash_count(catalog))
10638 const char *path = repos_rel_path;
10640 /* Start with the assumption there is no explicit or inherited
10641 mergeinfo for REPOS_REL_PATH in CATALOG. */
10642 svn_mergeinfo_t mergeinfo_in_cat = NULL;
10646 mergeinfo_in_cat = svn_hash_gets(catalog, path);
10648 if (mergeinfo_in_cat) /* Found it! */
10650 *cat_key_path = apr_pstrdup(result_pool, path);
10653 else /* Look for inherited mergeinfo. */
10655 walk_path = svn_relpath_join(svn_relpath_basename(path,
10657 walk_path ? walk_path : "",
10659 path = svn_relpath_dirname(path, scratch_pool);
10661 if (path[0] == '\0') /* No mergeinfo to inherit. */
10666 if (mergeinfo_in_cat)
10669 SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(&mergeinfo_in_cat,
10674 SVN_ERR(svn_mergeinfo_intersect2(&mergeinfo_in_cat,
10675 mergeinfo_in_cat, mergeinfo,
10677 scratch_pool, scratch_pool));
10678 SVN_ERR(svn_mergeinfo__equals(in_catalog, mergeinfo_in_cat,
10679 mergeinfo, TRUE, scratch_pool));
10683 return SVN_NO_ERROR;
10686 /* A svn_log_entry_receiver_t baton for log_find_operative_revs(). */
10687 typedef struct log_find_operative_baton_t
10689 /* The catalog of explicit mergeinfo on a reintegrate source. */
10690 svn_mergeinfo_catalog_t merged_catalog;
10692 /* The catalog of unmerged history from the reintegrate target to
10693 the source which we will create. Allocated in RESULT_POOL. */
10694 svn_mergeinfo_catalog_t unmerged_catalog;
10696 /* The repository absolute path of the reintegrate target. */
10697 const char *target_fspath;
10699 /* The path of the reintegrate source relative to the repository root. */
10700 const char *source_repos_rel_path;
10702 apr_pool_t *result_pool;
10703 } log_find_operative_baton_t;
10705 /* A svn_log_entry_receiver_t callback for find_unsynced_ranges(). */
10706 static svn_error_t *
10707 log_find_operative_revs(void *baton,
10708 svn_log_entry_t *log_entry,
10711 log_find_operative_baton_t *log_baton = baton;
10712 apr_hash_index_t *hi;
10713 svn_revnum_t revision;
10715 /* It's possible that authz restrictions on the merge source prevent us
10716 from knowing about any of the changes for LOG_ENTRY->REVISION. */
10717 if (!log_entry->changed_paths2)
10718 return SVN_NO_ERROR;
10720 revision = log_entry->revision;
10722 for (hi = apr_hash_first(pool, log_entry->changed_paths2);
10724 hi = apr_hash_next(hi))
10726 const char *subtree_missing_this_rev;
10727 const char *path = svn__apr_hash_index_key(hi);
10728 const char *rel_path;
10729 const char *source_rel_path;
10730 svn_boolean_t in_catalog;
10731 svn_mergeinfo_t log_entry_as_mergeinfo;
10733 rel_path = svn_fspath__skip_ancestor(log_baton->target_fspath, path);
10734 /* Easy out: The path is not within the tree of interest. */
10735 if (rel_path == NULL)
10738 source_rel_path = svn_relpath_join(log_baton->source_repos_rel_path,
10741 SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo,
10742 apr_psprintf(pool, "%s:%ld",
10746 SVN_ERR(mergeinfo_in_catalog(&in_catalog, &subtree_missing_this_rev,
10747 source_rel_path, log_entry_as_mergeinfo,
10748 log_baton->merged_catalog,
10753 svn_mergeinfo_t unmerged_for_key;
10754 const char *suffix, *missing_path;
10756 /* If there is no mergeinfo on the source tree we'll say
10757 the "subtree" missing this revision is the root of the
10759 if (!subtree_missing_this_rev)
10760 subtree_missing_this_rev = log_baton->source_repos_rel_path;
10762 suffix = svn_relpath_skip_ancestor(subtree_missing_this_rev,
10764 if (suffix && suffix[0] != '\0')
10766 missing_path = apr_pstrmemdup(pool, path,
10767 strlen(path) - strlen(suffix) - 1);
10771 missing_path = path;
10774 SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo,
10775 apr_psprintf(pool, "%s:%ld",
10776 missing_path, revision),
10777 log_baton->result_pool));
10778 unmerged_for_key = svn_hash_gets(log_baton->unmerged_catalog,
10779 subtree_missing_this_rev);
10781 if (unmerged_for_key)
10783 SVN_ERR(svn_mergeinfo_merge2(unmerged_for_key,
10784 log_entry_as_mergeinfo,
10785 log_baton->result_pool,
10790 svn_hash_sets(log_baton->unmerged_catalog,
10791 apr_pstrdup(log_baton->result_pool,
10792 subtree_missing_this_rev),
10793 log_entry_as_mergeinfo);
10798 return SVN_NO_ERROR;
10801 /* Determine if the mergeinfo on a reintegrate source SOURCE_LOC,
10802 reflects that the source is fully synced with the reintegrate target
10803 TARGET_LOC, even if a naive interpretation of the source's
10804 mergeinfo says otherwise -- See issue #3577.
10806 UNMERGED_CATALOG represents the history (as mergeinfo) from
10807 TARGET_LOC that is not represented in SOURCE_LOC's
10808 explicit/inherited mergeinfo as represented by MERGED_CATALOG.
10809 MERGED_CATALOG may be empty if the source has no explicit or inherited
10812 Check that all of the unmerged revisions in UNMERGED_CATALOG's
10813 mergeinfos are "phantoms", that is, one of the following conditions holds:
10815 1) The revision affects no corresponding paths in SOURCE_LOC.
10817 2) The revision affects corresponding paths in SOURCE_LOC,
10818 but based on the mergeinfo in MERGED_CATALOG, the change was
10821 Make a deep copy, allocated in RESULT_POOL, of any portions of
10822 UNMERGED_CATALOG that are not phantoms, to TRUE_UNMERGED_CATALOG.
10824 Note: The keys in all mergeinfo catalogs used here are relative to the
10825 root of the repository.
10827 RA_SESSION is an RA session open to the repository of TARGET_LOC; it may
10828 be temporarily reparented within this function.
10830 Use SCRATCH_POOL for all temporary allocations. */
10831 static svn_error_t *
10832 find_unsynced_ranges(const svn_client__pathrev_t *source_loc,
10833 const svn_client__pathrev_t *target_loc,
10834 svn_mergeinfo_catalog_t unmerged_catalog,
10835 svn_mergeinfo_catalog_t merged_catalog,
10836 svn_mergeinfo_catalog_t true_unmerged_catalog,
10837 svn_ra_session_t *ra_session,
10838 apr_pool_t *result_pool,
10839 apr_pool_t *scratch_pool)
10841 svn_rangelist_t *potentially_unmerged_ranges = NULL;
10843 /* Convert all the unmerged history to a rangelist. */
10844 if (apr_hash_count(unmerged_catalog))
10846 apr_hash_index_t *hi_catalog;
10848 potentially_unmerged_ranges =
10849 apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
10851 for (hi_catalog = apr_hash_first(scratch_pool, unmerged_catalog);
10853 hi_catalog = apr_hash_next(hi_catalog))
10855 svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi_catalog);
10857 SVN_ERR(svn_rangelist__merge_many(potentially_unmerged_ranges,
10859 scratch_pool, scratch_pool));
10863 /* Find any unmerged revisions which both affect the source and
10864 are not yet merged to it. */
10865 if (potentially_unmerged_ranges)
10867 svn_revnum_t oldest_rev =
10868 (APR_ARRAY_IDX(potentially_unmerged_ranges,
10870 svn_merge_range_t *))->start + 1;
10871 svn_revnum_t youngest_rev =
10872 (APR_ARRAY_IDX(potentially_unmerged_ranges,
10873 potentially_unmerged_ranges->nelts - 1,
10874 svn_merge_range_t *))->end;
10875 log_find_operative_baton_t log_baton;
10876 const char *old_session_url;
10879 log_baton.merged_catalog = merged_catalog;
10880 log_baton.unmerged_catalog = true_unmerged_catalog;
10881 log_baton.source_repos_rel_path
10882 = svn_client__pathrev_relpath(source_loc, scratch_pool);
10883 log_baton.target_fspath
10884 = svn_client__pathrev_fspath(target_loc, scratch_pool);
10885 log_baton.result_pool = result_pool;
10887 SVN_ERR(svn_client__ensure_ra_session_url(
10888 &old_session_url, ra_session, target_loc->url, scratch_pool));
10889 err = get_log(ra_session, "", youngest_rev, oldest_rev,
10890 TRUE, /* discover_changed_paths */
10891 log_find_operative_revs, &log_baton,
10893 SVN_ERR(svn_error_compose_create(
10894 err, svn_ra_reparent(ra_session, old_session_url, scratch_pool)));
10897 return SVN_NO_ERROR;
10901 /* Find the youngest revision that has been merged from target to source.
10903 * If any location in TARGET_HISTORY_AS_MERGEINFO is mentioned in
10904 * SOURCE_MERGEINFO, then we know that at least one merge was done from the
10905 * target to the source. In that case, set *YOUNGEST_MERGED_REV to the
10906 * youngest revision of that intersection (unless *YOUNGEST_MERGED_REV is
10907 * already younger than that). Otherwise, leave *YOUNGEST_MERGED_REV alone.
10909 static svn_error_t *
10910 find_youngest_merged_rev(svn_revnum_t *youngest_merged_rev,
10911 svn_mergeinfo_t target_history_as_mergeinfo,
10912 svn_mergeinfo_t source_mergeinfo,
10913 apr_pool_t *scratch_pool)
10915 svn_mergeinfo_t explicit_source_target_history_intersection;
10917 SVN_ERR(svn_mergeinfo_intersect2(
10918 &explicit_source_target_history_intersection,
10919 source_mergeinfo, target_history_as_mergeinfo, TRUE,
10920 scratch_pool, scratch_pool));
10921 if (apr_hash_count(explicit_source_target_history_intersection))
10923 svn_revnum_t old_rev, young_rev;
10925 /* Keep track of the youngest revision merged from target to source. */
10926 SVN_ERR(svn_mergeinfo__get_range_endpoints(
10927 &young_rev, &old_rev,
10928 explicit_source_target_history_intersection, scratch_pool));
10929 if (!SVN_IS_VALID_REVNUM(*youngest_merged_rev)
10930 || (young_rev > *youngest_merged_rev))
10931 *youngest_merged_rev = young_rev;
10934 return SVN_NO_ERROR;
10937 /* Set *FILTERED_MERGEINFO_P to the parts of TARGET_HISTORY_AS_MERGEINFO
10938 * that are not present in the source branch.
10940 * SOURCE_MERGEINFO is the explicit or inherited mergeinfo of the source
10941 * branch SOURCE_PATHREV. Extend SOURCE_MERGEINFO, modifying it in
10942 * place, to include the natural history (implicit mergeinfo) of
10943 * SOURCE_PATHREV. ### But make these additions in SCRATCH_POOL.
10945 * SOURCE_RA_SESSION is an RA session open to the repository containing
10946 * SOURCE_PATHREV; it may be temporarily reparented within this function.
10948 * ### [JAF] This function is named '..._subroutine' simply because I
10949 * factored it out based on code similarity, without knowing what it's
10950 * purpose is. We should clarify its purpose and choose a better name.
10952 static svn_error_t *
10953 find_unmerged_mergeinfo_subroutine(svn_mergeinfo_t *filtered_mergeinfo_p,
10954 svn_mergeinfo_t target_history_as_mergeinfo,
10955 svn_mergeinfo_t source_mergeinfo,
10956 const svn_client__pathrev_t *source_pathrev,
10957 svn_ra_session_t *source_ra_session,
10958 svn_client_ctx_t *ctx,
10959 apr_pool_t *result_pool,
10960 apr_pool_t *scratch_pool)
10962 svn_mergeinfo_t source_history_as_mergeinfo;
10964 /* Get the source path's natural history and merge it into source
10965 path's explicit or inherited mergeinfo. */
10966 SVN_ERR(svn_client__get_history_as_mergeinfo(
10967 &source_history_as_mergeinfo, NULL /* has_rev_zero_history */,
10968 source_pathrev, source_pathrev->rev, SVN_INVALID_REVNUM,
10969 source_ra_session, ctx, scratch_pool));
10970 SVN_ERR(svn_mergeinfo_merge2(source_mergeinfo,
10971 source_history_as_mergeinfo,
10972 scratch_pool, scratch_pool));
10974 /* Now source_mergeinfo represents everything we know about
10975 source_path's history. Now we need to know what part, if any, of the
10976 corresponding target's history is *not* part of source_path's total
10977 history; because it is neither shared history nor was it ever merged
10978 from the target to the source. */
10979 SVN_ERR(svn_mergeinfo_remove2(filtered_mergeinfo_p,
10981 target_history_as_mergeinfo, TRUE,
10982 result_pool, scratch_pool));
10983 return SVN_NO_ERROR;
10986 /* Helper for calculate_left_hand_side() which produces a mergeinfo catalog
10987 describing what parts of of the reintegrate target have not previously been
10988 merged to the reintegrate source.
10990 SOURCE_CATALOG is the collection of explicit mergeinfo on SOURCE_LOC and
10991 all its children, i.e. the mergeinfo catalog for the reintegrate source.
10993 TARGET_HISTORY_HASH is a hash of (const char *) paths mapped to
10994 svn_mergeinfo_t representing the location history. Each of these
10995 path keys represent a path in the reintegrate target, relative to the
10996 repository root, which has explicit mergeinfo and/or is the reintegrate
10997 target itself. The svn_mergeinfo_t's contain the natural history of each
10998 path@TARGET_REV. Effectively this is the mergeinfo catalog on the
10999 reintegrate target.
11001 YC_ANCESTOR_REV is the revision of the youngest common ancestor of the
11002 reintegrate source and the reintegrate target.
11004 SOURCE_LOC is the reintegrate source.
11006 SOURCE_RA_SESSION is a session opened to the URL of SOURCE_LOC
11007 and TARGET_RA_SESSION is open to TARGET->loc.url.
11009 For each entry in TARGET_HISTORY_HASH check that the history it
11010 represents is contained in either the explicit mergeinfo for the
11011 corresponding path in SOURCE_CATALOG, the corresponding path's inherited
11012 mergeinfo (if no explicit mergeinfo for the path is found in
11013 SOURCE_CATALOG), or the corresponding path's natural history. Populate
11014 *UNMERGED_TO_SOURCE_CATALOG with the corresponding source paths mapped to
11015 the mergeinfo from the target's natural history which is *not* found. Also
11016 include any mergeinfo from SOURCE_CATALOG which explicitly describes the
11017 target's history but for which *no* entry was found in
11018 TARGET_HISTORY_HASH.
11020 If no part of TARGET_HISTORY_HASH is found in SOURCE_CATALOG set
11021 *YOUNGEST_MERGED_REV to SVN_INVALID_REVNUM; otherwise set it to the youngest
11022 revision previously merged from the target to the source, and filter
11023 *UNMERGED_TO_SOURCE_CATALOG so that it contains no ranges greater than
11024 *YOUNGEST_MERGED_REV.
11026 *UNMERGED_TO_SOURCE_CATALOG is (deeply) allocated in RESULT_POOL.
11027 SCRATCH_POOL is used for all temporary allocations. */
11028 static svn_error_t *
11029 find_unmerged_mergeinfo(svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
11030 svn_revnum_t *youngest_merged_rev,
11031 svn_revnum_t yc_ancestor_rev,
11032 svn_mergeinfo_catalog_t source_catalog,
11033 apr_hash_t *target_history_hash,
11034 const svn_client__pathrev_t *source_loc,
11035 const merge_target_t *target,
11036 svn_ra_session_t *source_ra_session,
11037 svn_ra_session_t *target_ra_session,
11038 svn_client_ctx_t *ctx,
11039 apr_pool_t *result_pool,
11040 apr_pool_t *scratch_pool)
11042 const char *source_repos_rel_path
11043 = svn_client__pathrev_relpath(source_loc, scratch_pool);
11044 const char *target_repos_rel_path
11045 = svn_client__pathrev_relpath(&target->loc, scratch_pool);
11046 apr_hash_index_t *hi;
11047 svn_mergeinfo_catalog_t new_catalog = apr_hash_make(result_pool);
11048 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
11050 assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11051 assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11053 *youngest_merged_rev = SVN_INVALID_REVNUM;
11055 /* Examine the natural history of each path in the reintegrate target
11056 with explicit mergeinfo. */
11057 for (hi = apr_hash_first(scratch_pool, target_history_hash);
11059 hi = apr_hash_next(hi))
11061 const char *target_path = svn__apr_hash_index_key(hi);
11062 svn_mergeinfo_t target_history_as_mergeinfo = svn__apr_hash_index_val(hi);
11063 const char *path_rel_to_session
11064 = svn_relpath_skip_ancestor(target_repos_rel_path, target_path);
11065 const char *source_path;
11066 svn_client__pathrev_t *source_pathrev;
11067 svn_mergeinfo_t source_mergeinfo, filtered_mergeinfo;
11069 svn_pool_clear(iterpool);
11071 source_path = svn_relpath_join(source_repos_rel_path,
11072 path_rel_to_session, iterpool);
11073 source_pathrev = svn_client__pathrev_join_relpath(
11074 source_loc, path_rel_to_session, iterpool);
11076 /* Remove any target history that is also part of the source's history,
11077 i.e. their common ancestry. By definition this has already been
11078 "merged" from the target to the source. If the source has explicit
11079 self referential mergeinfo it would intersect with the target's
11080 history below, making it appear that some merges had been done from
11081 the target to the source, when this might not actually be the case. */
11082 SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
11083 &target_history_as_mergeinfo, target_history_as_mergeinfo,
11084 source_loc->rev, yc_ancestor_rev, TRUE, iterpool, iterpool));
11086 /* Look for any explicit mergeinfo on the source path corresponding to
11087 the target path. If we find any remove that from SOURCE_CATALOG.
11088 When this iteration over TARGET_HISTORY_HASH is complete all that
11089 should be left in SOURCE_CATALOG are subtrees that have explicit
11090 mergeinfo on the reintegrate source where there is no corresponding
11091 explicit mergeinfo on the reintegrate target. */
11092 source_mergeinfo = svn_hash_gets(source_catalog, source_path);
11093 if (source_mergeinfo)
11095 svn_hash_sets(source_catalog, source_path, NULL);
11097 SVN_ERR(find_youngest_merged_rev(youngest_merged_rev,
11098 target_history_as_mergeinfo,
11104 /* There is no mergeinfo on source_path *or* source_path doesn't
11105 exist at all. If simply doesn't exist we can ignore it
11107 svn_node_kind_t kind;
11109 SVN_ERR(svn_ra_check_path(source_ra_session,
11110 path_rel_to_session,
11111 source_loc->rev, &kind, iterpool));
11112 if (kind == svn_node_none)
11114 /* Else source_path does exist though it has no explicit mergeinfo.
11115 Find its inherited mergeinfo. If it doesn't have any then simply
11116 set source_mergeinfo to an empty hash. */
11117 SVN_ERR(svn_client__get_repos_mergeinfo(
11118 &source_mergeinfo, source_ra_session,
11119 source_pathrev->url, source_pathrev->rev,
11120 svn_mergeinfo_inherited, FALSE /*squelch_incapable*/,
11122 if (!source_mergeinfo)
11123 source_mergeinfo = apr_hash_make(iterpool);
11126 /* Use scratch_pool rather than iterpool because filtered_mergeinfo
11127 is going into new_catalog below and needs to last to the end of
11129 SVN_ERR(find_unmerged_mergeinfo_subroutine(
11130 &filtered_mergeinfo, target_history_as_mergeinfo,
11131 source_mergeinfo, source_pathrev,
11132 source_ra_session, ctx, scratch_pool, iterpool));
11133 svn_hash_sets(new_catalog, apr_pstrdup(scratch_pool, source_path),
11134 filtered_mergeinfo);
11137 /* Are there any subtrees with explicit mergeinfo still left in the merge
11138 source where there was no explicit mergeinfo for the corresponding path
11139 in the merge target? If so, add the intersection of those path's
11140 mergeinfo and the corresponding target path's mergeinfo to
11142 for (hi = apr_hash_first(scratch_pool, source_catalog);
11144 hi = apr_hash_next(hi))
11146 const char *source_path = svn__apr_hash_index_key(hi);
11147 const char *path_rel_to_session =
11148 svn_relpath_skip_ancestor(source_repos_rel_path, source_path);
11149 const char *source_url;
11150 svn_mergeinfo_t source_mergeinfo = svn__apr_hash_index_val(hi);
11151 svn_mergeinfo_t filtered_mergeinfo;
11152 svn_client__pathrev_t *target_pathrev;
11153 svn_mergeinfo_t target_history_as_mergeinfo;
11156 svn_pool_clear(iterpool);
11158 source_url = svn_path_url_add_component2(source_loc->url,
11159 path_rel_to_session, iterpool);
11160 target_pathrev = svn_client__pathrev_join_relpath(
11161 &target->loc, path_rel_to_session, iterpool);
11162 err = svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo,
11163 NULL /* has_rev_zero_history */,
11166 SVN_INVALID_REVNUM,
11171 if (err->apr_err == SVN_ERR_FS_NOT_FOUND
11172 || err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
11174 /* This path with explicit mergeinfo in the source doesn't
11175 exist on the target. */
11176 svn_error_clear(err);
11181 return svn_error_trace(err);
11186 svn_client__pathrev_t *pathrev;
11188 SVN_ERR(find_youngest_merged_rev(youngest_merged_rev,
11189 target_history_as_mergeinfo,
11193 /* Use scratch_pool rather than iterpool because filtered_mergeinfo
11194 is going into new_catalog below and needs to last to the end of
11196 /* ### Why looking at SOURCE_url at TARGET_rev? */
11197 SVN_ERR(svn_client__pathrev_create_with_session(
11198 &pathrev, source_ra_session, target->loc.rev, source_url,
11200 SVN_ERR(find_unmerged_mergeinfo_subroutine(
11201 &filtered_mergeinfo, target_history_as_mergeinfo,
11202 source_mergeinfo, pathrev,
11203 source_ra_session, ctx, scratch_pool, iterpool));
11204 if (apr_hash_count(filtered_mergeinfo))
11205 svn_hash_sets(new_catalog,
11206 apr_pstrdup(scratch_pool, source_path),
11207 filtered_mergeinfo);
11211 /* Limit new_catalog to the youngest revisions previously merged from
11212 the target to the source. */
11213 if (SVN_IS_VALID_REVNUM(*youngest_merged_rev))
11214 SVN_ERR(svn_mergeinfo__filter_catalog_by_ranges(&new_catalog,
11216 *youngest_merged_rev,
11217 0, /* No oldest bound. */
11222 /* Make a shiny new copy before blowing away all the temporary pools. */
11223 *unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(new_catalog,
11225 svn_pool_destroy(iterpool);
11226 return SVN_NO_ERROR;
11229 /* Helper for svn_client_merge_reintegrate() which calculates the
11230 'left hand side' of the underlying two-URL merge that a --reintegrate
11231 merge actually performs. If no merge should be performed, set
11234 TARGET->abspath is the absolute working copy path of the reintegrate
11237 SOURCE_LOC is the reintegrate source.
11239 SUBTREES_WITH_MERGEINFO is a hash of (const char *) absolute paths mapped
11240 to (svn_mergeinfo_t *) mergeinfo values for each working copy path with
11241 explicit mergeinfo in TARGET->abspath. Actually we only need to know the
11242 paths, not the mergeinfo.
11244 TARGET->loc.rev is the working revision the entire WC tree rooted at
11247 Populate *UNMERGED_TO_SOURCE_CATALOG with the mergeinfo describing what
11248 parts of TARGET->loc have not been merged to SOURCE_LOC, up to the
11249 youngest revision ever merged from the TARGET->abspath to the source if
11250 such exists, see doc string for find_unmerged_mergeinfo().
11252 SOURCE_RA_SESSION is a session opened to the SOURCE_LOC
11253 and TARGET_RA_SESSION is open to TARGET->loc.url.
11255 *LEFT_P, *MERGED_TO_SOURCE_CATALOG , and *UNMERGED_TO_SOURCE_CATALOG are
11256 allocated in RESULT_POOL. SCRATCH_POOL is used for all temporary
11258 static svn_error_t *
11259 calculate_left_hand_side(svn_client__pathrev_t **left_p,
11260 svn_mergeinfo_catalog_t *merged_to_source_catalog,
11261 svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
11262 const merge_target_t *target,
11263 apr_hash_t *subtrees_with_mergeinfo,
11264 const svn_client__pathrev_t *source_loc,
11265 svn_ra_session_t *source_ra_session,
11266 svn_ra_session_t *target_ra_session,
11267 svn_client_ctx_t *ctx,
11268 apr_pool_t *result_pool,
11269 apr_pool_t *scratch_pool)
11271 svn_mergeinfo_catalog_t mergeinfo_catalog, unmerged_catalog;
11272 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
11273 apr_hash_index_t *hi;
11274 /* hash of paths mapped to arrays of svn_mergeinfo_t. */
11275 apr_hash_t *target_history_hash = apr_hash_make(scratch_pool);
11276 svn_revnum_t youngest_merged_rev;
11277 svn_client__pathrev_t *yc_ancestor;
11279 assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11280 assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11282 /* Initialize our return variables. */
11285 /* TARGET->abspath may not have explicit mergeinfo and thus may not be
11286 contained within SUBTREES_WITH_MERGEINFO. If this is the case then
11287 add a dummy item for TARGET->abspath so we get its history (i.e. implicit
11288 mergeinfo) below. */
11289 if (!svn_hash_gets(subtrees_with_mergeinfo, target->abspath))
11290 svn_hash_sets(subtrees_with_mergeinfo, target->abspath,
11291 apr_hash_make(result_pool));
11293 /* Get the history segments (as mergeinfo) for TARGET->abspath and any of
11294 its subtrees with explicit mergeinfo. */
11295 for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
11297 hi = apr_hash_next(hi))
11299 const char *local_abspath = svn__apr_hash_index_key(hi);
11300 svn_client__pathrev_t *target_child;
11301 const char *repos_relpath;
11302 svn_mergeinfo_t target_history_as_mergeinfo;
11304 svn_pool_clear(iterpool);
11306 /* Convert the absolute path with mergeinfo on it to a path relative
11307 to the session root. */
11308 SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, NULL, NULL,
11309 ctx->wc_ctx, local_abspath,
11310 scratch_pool, iterpool));
11311 target_child = svn_client__pathrev_create_with_relpath(
11312 target->loc.repos_root_url, target->loc.repos_uuid,
11313 target->loc.rev, repos_relpath, iterpool);
11314 SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo,
11315 NULL /* has_rev_zero_hist */,
11318 SVN_INVALID_REVNUM,
11320 ctx, scratch_pool));
11322 svn_hash_sets(target_history_hash, repos_relpath,
11323 target_history_as_mergeinfo);
11326 /* Check that SOURCE_LOC and TARGET->loc are
11327 actually related, we can't reintegrate if they are not. Also
11328 get an initial value for the YCA revision number. */
11329 SVN_ERR(svn_client__get_youngest_common_ancestor(
11330 &yc_ancestor, source_loc, &target->loc, target_ra_session, ctx,
11331 iterpool, iterpool));
11333 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11334 _("'%s@%ld' must be ancestrally related to "
11335 "'%s@%ld'"), source_loc->url, source_loc->rev,
11336 target->loc.url, target->loc.rev);
11338 /* If the source revision is the same as the youngest common
11339 revision, then there can't possibly be any unmerged revisions
11340 that we need to apply to target. */
11341 if (source_loc->rev == yc_ancestor->rev)
11343 svn_pool_destroy(iterpool);
11344 return SVN_NO_ERROR;
11347 /* Get the mergeinfo from the source, including its descendants
11348 with differing explicit mergeinfo. */
11349 SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
11350 &mergeinfo_catalog, source_ra_session,
11351 source_loc->url, source_loc->rev,
11352 svn_mergeinfo_inherited, FALSE /* squelch_incapable */,
11353 TRUE /* include_descendants */, iterpool, iterpool));
11355 if (!mergeinfo_catalog)
11356 mergeinfo_catalog = apr_hash_make(iterpool);
11358 *merged_to_source_catalog = svn_mergeinfo_catalog_dup(mergeinfo_catalog,
11361 /* Filter the source's mergeinfo catalog so that we are left with
11362 mergeinfo that describes what has *not* previously been merged from
11363 TARGET->loc to SOURCE_LOC. */
11364 SVN_ERR(find_unmerged_mergeinfo(&unmerged_catalog,
11365 &youngest_merged_rev,
11368 target_history_hash,
11374 iterpool, iterpool));
11376 /* Simplify unmerged_catalog through elision then make a copy in POOL. */
11377 SVN_ERR(svn_client__elide_mergeinfo_catalog(unmerged_catalog,
11379 *unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(unmerged_catalog,
11382 if (youngest_merged_rev == SVN_INVALID_REVNUM)
11384 /* We never merged to the source. Just return the branch point. */
11385 *left_p = svn_client__pathrev_dup(yc_ancestor, result_pool);
11389 /* We've previously merged some or all of the target, up to
11390 youngest_merged_rev, to the source. Set
11391 *LEFT_P to cover the youngest part of this range. */
11392 SVN_ERR(svn_client__repos_location(left_p, target_ra_session,
11393 &target->loc, youngest_merged_rev,
11394 ctx, result_pool, iterpool));
11397 svn_pool_destroy(iterpool);
11398 return SVN_NO_ERROR;
11401 /* Determine the URLs and revisions needed to perform a reintegrate merge
11402 * from SOURCE_LOC into the working copy at TARGET.
11404 * SOURCE_RA_SESSION and TARGET_RA_SESSION are RA sessions opened to the
11405 * URLs of SOURCE_LOC and TARGET->loc respectively.
11408 * the source-left and source-right locations of the required merge. Set
11409 * *YC_ANCESTOR_P to the location of the youngest ancestor.
11410 * Any of these output pointers may be NULL if not wanted.
11412 * See svn_client_find_reintegrate_merge() for other details.
11414 static svn_error_t *
11415 find_reintegrate_merge(merge_source_t **source_p,
11416 svn_client__pathrev_t **yc_ancestor_p,
11417 svn_ra_session_t *source_ra_session,
11418 const svn_client__pathrev_t *source_loc,
11419 svn_ra_session_t *target_ra_session,
11420 const merge_target_t *target,
11421 svn_client_ctx_t *ctx,
11422 apr_pool_t *result_pool,
11423 apr_pool_t *scratch_pool)
11425 svn_client__pathrev_t *yc_ancestor;
11426 svn_client__pathrev_t *loc1;
11427 merge_source_t source;
11428 svn_mergeinfo_catalog_t unmerged_to_source_mergeinfo_catalog;
11429 svn_mergeinfo_catalog_t merged_to_source_mergeinfo_catalog;
11431 apr_hash_t *subtrees_with_mergeinfo;
11433 assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11434 assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11436 /* As the WC tree is "pure", use its last-updated-to revision as
11437 the default revision for the left side of our merge, since that's
11438 what the repository sub-tree is required to be up to date with
11439 (with regard to the WC). */
11440 /* ### Bogus/obsolete comment? */
11442 /* Can't reintegrate to or from the root of the repository. */
11443 if (strcmp(source_loc->url, source_loc->repos_root_url) == 0
11444 || strcmp(target->loc.url, target->loc.repos_root_url) == 0)
11445 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11446 _("Neither the reintegrate source nor target "
11447 "can be the root of the repository"));
11449 /* Find all the subtrees in TARGET_WCPATH that have explicit mergeinfo. */
11450 err = get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
11451 target->abspath, svn_depth_infinity,
11452 ctx, scratch_pool, scratch_pool);
11453 /* Issue #3896: If invalid mergeinfo in the reintegrate target
11454 prevents us from proceeding, then raise the best error possible. */
11455 if (err && err->apr_err == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
11456 err = svn_error_quick_wrap(err, _("Reintegrate merge not possible"));
11459 SVN_ERR(calculate_left_hand_side(&loc1,
11460 &merged_to_source_mergeinfo_catalog,
11461 &unmerged_to_source_mergeinfo_catalog,
11463 subtrees_with_mergeinfo,
11468 scratch_pool, scratch_pool));
11470 /* Did calculate_left_hand_side() decide that there was no merge to
11471 be performed here? */
11477 *yc_ancestor_p = NULL;
11478 return SVN_NO_ERROR;
11481 source.loc1 = loc1;
11482 source.loc2 = source_loc;
11484 /* If the target was moved after the source was branched from it,
11485 it is possible that the left URL differs from the target's current
11486 URL. If so, then adjust TARGET_RA_SESSION to point to the old URL. */
11487 if (strcmp(source.loc1->url, target->loc.url))
11488 SVN_ERR(svn_ra_reparent(target_ra_session, source.loc1->url, scratch_pool));
11490 SVN_ERR(svn_client__get_youngest_common_ancestor(
11491 &yc_ancestor, source.loc2, source.loc1, target_ra_session,
11492 ctx, scratch_pool, scratch_pool));
11495 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11496 _("'%s@%ld' must be ancestrally related to "
11498 source.loc1->url, source.loc1->rev,
11499 source.loc2->url, source.loc2->rev);
11501 /* The source side of a reintegrate merge is not 'ancestral', except in
11502 * the degenerate case where source == YCA. */
11503 source.ancestral = (loc1->rev == yc_ancestor->rev);
11505 if (source.loc1->rev > yc_ancestor->rev)
11507 /* Have we actually merged anything to the source from the
11508 target? If so, make sure we've merged a contiguous
11510 svn_mergeinfo_catalog_t final_unmerged_catalog = apr_hash_make(scratch_pool);
11512 SVN_ERR(find_unsynced_ranges(source_loc, &target->loc,
11513 unmerged_to_source_mergeinfo_catalog,
11514 merged_to_source_mergeinfo_catalog,
11515 final_unmerged_catalog,
11516 target_ra_session, scratch_pool,
11519 if (apr_hash_count(final_unmerged_catalog))
11521 svn_string_t *source_mergeinfo_cat_string;
11523 SVN_ERR(svn_mergeinfo__catalog_to_formatted_string(
11524 &source_mergeinfo_cat_string,
11525 final_unmerged_catalog,
11526 " ", " Missing ranges: ", scratch_pool));
11527 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
11529 _("Reintegrate can only be used if "
11530 "revisions %ld through %ld were "
11531 "previously merged from %s to the "
11532 "reintegrate source, but this is "
11533 "not the case:\n%s"),
11534 yc_ancestor->rev + 1, source.loc2->rev,
11536 source_mergeinfo_cat_string->data);
11540 /* Left side: trunk@youngest-trunk-rev-merged-to-branch-at-specified-peg-rev
11541 * Right side: branch@specified-peg-revision */
11543 *source_p = merge_source_dup(&source, result_pool);
11546 *yc_ancestor_p = svn_client__pathrev_dup(yc_ancestor, result_pool);
11547 return SVN_NO_ERROR;
11550 /* Resolve the source and target locations and open RA sessions to them, and
11551 * perform some checks appropriate for a reintegrate merge.
11553 * Set *SOURCE_RA_SESSION_P and *SOURCE_LOC_P to a new session and the
11554 * repository location of SOURCE_PATH_OR_URL at SOURCE_PEG_REVISION. Set
11555 * *TARGET_RA_SESSION_P and *TARGET_P to a new session and the repository
11556 * location of the WC at TARGET_ABSPATH.
11558 * Throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES error if the target WC node is
11559 * a locally added node or if the source and target are not in the same
11560 * repository. Throw a SVN_ERR_CLIENT_NOT_READY_TO_MERGE error if the
11561 * target WC is not at a single revision without switched subtrees and
11562 * without local mods.
11564 * Allocate all the outputs in RESULT_POOL.
11566 static svn_error_t *
11567 open_reintegrate_source_and_target(svn_ra_session_t **source_ra_session_p,
11568 svn_client__pathrev_t **source_loc_p,
11569 svn_ra_session_t **target_ra_session_p,
11570 merge_target_t **target_p,
11571 const char *source_path_or_url,
11572 const svn_opt_revision_t *source_peg_revision,
11573 const char *target_abspath,
11574 svn_client_ctx_t *ctx,
11575 apr_pool_t *result_pool,
11576 apr_pool_t *scratch_pool)
11578 svn_client__pathrev_t *source_loc;
11579 merge_target_t *target;
11581 /* Open the target WC. A reintegrate merge requires the merge target to
11582 * reflect a subtree of the repository as found at a single revision. */
11583 SVN_ERR(open_target_wc(&target, target_abspath,
11584 FALSE, FALSE, FALSE,
11585 ctx, scratch_pool, scratch_pool));
11586 SVN_ERR(svn_client_open_ra_session2(target_ra_session_p,
11587 target->loc.url, target->abspath,
11588 ctx, result_pool, scratch_pool));
11589 if (! target->loc.url)
11590 return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
11591 _("Can't reintegrate into '%s' because it is "
11592 "locally added and therefore not related to "
11593 "the merge source"),
11594 svn_dirent_local_style(target->abspath,
11597 SVN_ERR(svn_client__ra_session_from_path2(
11598 source_ra_session_p, &source_loc,
11599 source_path_or_url, NULL, source_peg_revision, source_peg_revision,
11600 ctx, result_pool));
11602 /* source_loc and target->loc are required to be in the same repository,
11603 as mergeinfo doesn't come into play for cross-repository merging. */
11604 SVN_ERR(check_same_repos(source_loc,
11605 svn_dirent_local_style(source_path_or_url,
11608 svn_dirent_local_style(target->abspath,
11610 TRUE /* strict_urls */, scratch_pool));
11612 *source_loc_p = source_loc;
11613 *target_p = target;
11614 return SVN_NO_ERROR;
11617 /* The body of svn_client_merge_reintegrate(), which see for details. */
11618 static svn_error_t *
11619 merge_reintegrate_locked(conflict_report_t **conflict_report,
11620 const char *source_path_or_url,
11621 const svn_opt_revision_t *source_peg_revision,
11622 const char *target_abspath,
11623 svn_boolean_t diff_ignore_ancestry,
11624 svn_boolean_t dry_run,
11625 const apr_array_header_t *merge_options,
11626 svn_client_ctx_t *ctx,
11627 apr_pool_t *result_pool,
11628 apr_pool_t *scratch_pool)
11630 svn_ra_session_t *target_ra_session, *source_ra_session;
11631 merge_target_t *target;
11632 svn_client__pathrev_t *source_loc;
11633 merge_source_t *source;
11634 svn_client__pathrev_t *yc_ancestor;
11635 svn_boolean_t use_sleep = FALSE;
11638 SVN_ERR(open_reintegrate_source_and_target(
11639 &source_ra_session, &source_loc, &target_ra_session, &target,
11640 source_path_or_url, source_peg_revision, target_abspath,
11641 ctx, scratch_pool, scratch_pool));
11643 SVN_ERR(find_reintegrate_merge(&source, &yc_ancestor,
11644 source_ra_session, source_loc,
11645 target_ra_session, target,
11646 ctx, scratch_pool, scratch_pool));
11650 return SVN_NO_ERROR;
11653 /* Do the real merge! */
11654 /* ### TODO(reint): Make sure that one isn't the same line ancestor
11655 ### of the other (what's erroneously referred to as "ancestrally
11656 ### related" in this source file). For now, we just say the source
11657 ### isn't "ancestral" even if it is (in the degenerate case where
11658 ### source-left equals YCA). */
11659 source->ancestral = FALSE;
11660 err = merge_cousins_and_supplement_mergeinfo(conflict_report,
11665 source, yc_ancestor,
11666 TRUE /* same_repos */,
11667 svn_depth_infinity,
11668 diff_ignore_ancestry,
11669 FALSE /* force_delete */,
11670 FALSE /* record_only */,
11674 result_pool, scratch_pool);
11677 svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
11680 return SVN_NO_ERROR;
11684 svn_client_merge_reintegrate(const char *source_path_or_url,
11685 const svn_opt_revision_t *source_peg_revision,
11686 const char *target_wcpath,
11687 svn_boolean_t dry_run,
11688 const apr_array_header_t *merge_options,
11689 svn_client_ctx_t *ctx,
11692 const char *target_abspath, *lock_abspath;
11693 conflict_report_t *conflict_report;
11695 SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
11696 target_wcpath, ctx, pool));
11699 SVN_WC__CALL_WITH_WRITE_LOCK(
11700 merge_reintegrate_locked(&conflict_report,
11701 source_path_or_url, source_peg_revision,
11703 FALSE /*diff_ignore_ancestry*/,
11704 dry_run, merge_options, ctx, pool, pool),
11705 ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
11707 SVN_ERR(merge_reintegrate_locked(&conflict_report,
11708 source_path_or_url, source_peg_revision,
11710 FALSE /*diff_ignore_ancestry*/,
11711 dry_run, merge_options, ctx, pool, pool));
11713 SVN_ERR(make_merge_conflict_error(conflict_report, pool));
11714 return SVN_NO_ERROR;
11718 /* The body of svn_client_merge_peg5(), which see for details.
11720 * IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge().
11722 static svn_error_t *
11723 merge_peg_locked(conflict_report_t **conflict_report,
11724 const char *source_path_or_url,
11725 const svn_opt_revision_t *source_peg_revision,
11726 const svn_rangelist_t *ranges_to_merge,
11727 const char *target_abspath,
11729 svn_boolean_t ignore_mergeinfo,
11730 svn_boolean_t diff_ignore_ancestry,
11731 svn_boolean_t force_delete,
11732 svn_boolean_t record_only,
11733 svn_boolean_t dry_run,
11734 svn_boolean_t allow_mixed_rev,
11735 const apr_array_header_t *merge_options,
11736 svn_client_ctx_t *ctx,
11737 apr_pool_t *result_pool,
11738 apr_pool_t *scratch_pool)
11740 merge_target_t *target;
11741 svn_client__pathrev_t *source_loc;
11742 apr_array_header_t *merge_sources;
11743 svn_ra_session_t *ra_session;
11744 apr_pool_t *sesspool;
11745 svn_boolean_t use_sleep = FALSE;
11747 svn_boolean_t same_repos;
11749 SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
11751 SVN_ERR(open_target_wc(&target, target_abspath,
11752 allow_mixed_rev, TRUE, TRUE,
11753 ctx, scratch_pool, scratch_pool));
11755 /* Create a short lived session pool */
11756 sesspool = svn_pool_create(scratch_pool);
11758 /* Open an RA session to our source URL, and determine its root URL. */
11759 SVN_ERR(svn_client__ra_session_from_path2(
11760 &ra_session, &source_loc,
11761 source_path_or_url, NULL, source_peg_revision, source_peg_revision,
11764 /* Normalize our merge sources. */
11765 SVN_ERR(normalize_merge_sources(&merge_sources, source_path_or_url,
11767 ranges_to_merge, ra_session, ctx,
11768 scratch_pool, scratch_pool));
11770 /* Check for same_repos. */
11771 same_repos = is_same_repos(&target->loc, source_loc, TRUE /* strict_urls */);
11773 /* Do the real merge! (We say with confidence that our merge
11774 sources are both ancestral and related.) */
11775 err = do_merge(NULL, NULL, conflict_report, &use_sleep,
11776 merge_sources, target, ra_session,
11777 TRUE /*sources_related*/, same_repos, ignore_mergeinfo,
11778 diff_ignore_ancestry, force_delete, dry_run,
11779 record_only, NULL, FALSE, FALSE, depth, merge_options,
11780 ctx, result_pool, scratch_pool);
11782 /* We're done with our RA session. */
11783 svn_pool_destroy(sesspool);
11786 svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
11789 return SVN_NO_ERROR;
11792 /* Details of an automatic merge. */
11793 typedef struct automatic_merge_t
11795 svn_client__pathrev_t *yca, *base, *right, *target;
11796 svn_boolean_t is_reintegrate_like;
11797 svn_boolean_t allow_mixed_rev, allow_local_mods, allow_switched_subtrees;
11798 } automatic_merge_t;
11800 static svn_error_t *
11801 client_find_automatic_merge(automatic_merge_t **merge_p,
11802 const char *source_path_or_url,
11803 const svn_opt_revision_t *source_revision,
11804 const char *target_abspath,
11805 svn_boolean_t allow_mixed_rev,
11806 svn_boolean_t allow_local_mods,
11807 svn_boolean_t allow_switched_subtrees,
11808 svn_client_ctx_t *ctx,
11809 apr_pool_t *result_pool,
11810 apr_pool_t *scratch_pool);
11812 static svn_error_t *
11813 do_automatic_merge_locked(conflict_report_t **conflict_report,
11814 const automatic_merge_t *merge,
11815 const char *target_abspath,
11817 svn_boolean_t diff_ignore_ancestry,
11818 svn_boolean_t force_delete,
11819 svn_boolean_t record_only,
11820 svn_boolean_t dry_run,
11821 const apr_array_header_t *merge_options,
11822 svn_client_ctx_t *ctx,
11823 apr_pool_t *result_pool,
11824 apr_pool_t *scratch_pool);
11827 svn_client_merge_peg5(const char *source_path_or_url,
11828 const apr_array_header_t *ranges_to_merge,
11829 const svn_opt_revision_t *source_peg_revision,
11830 const char *target_wcpath,
11832 svn_boolean_t ignore_mergeinfo,
11833 svn_boolean_t diff_ignore_ancestry,
11834 svn_boolean_t force_delete,
11835 svn_boolean_t record_only,
11836 svn_boolean_t dry_run,
11837 svn_boolean_t allow_mixed_rev,
11838 const apr_array_header_t *merge_options,
11839 svn_client_ctx_t *ctx,
11842 const char *target_abspath, *lock_abspath;
11843 conflict_report_t *conflict_report;
11845 /* No ranges to merge? No problem. */
11846 if (ranges_to_merge != NULL && ranges_to_merge->nelts == 0)
11847 return SVN_NO_ERROR;
11849 SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
11850 target_wcpath, ctx, pool));
11852 /* Do an automatic merge if no revision ranges are specified. */
11853 if (ranges_to_merge == NULL)
11855 automatic_merge_t *merge;
11857 if (ignore_mergeinfo)
11858 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
11859 _("Cannot merge automatically while "
11860 "ignoring mergeinfo"));
11862 /* Find the details of the merge needed. */
11863 SVN_ERR(client_find_automatic_merge(
11865 source_path_or_url, source_peg_revision,
11868 TRUE /*allow_local_mods*/,
11869 TRUE /*allow_switched_subtrees*/,
11873 SVN_WC__CALL_WITH_WRITE_LOCK(
11874 do_automatic_merge_locked(&conflict_report,
11876 target_abspath, depth,
11877 diff_ignore_ancestry,
11878 force_delete, record_only, dry_run,
11879 merge_options, ctx, pool, pool),
11880 ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
11882 SVN_ERR(do_automatic_merge_locked(&conflict_report,
11884 target_abspath, depth,
11885 diff_ignore_ancestry,
11886 force_delete, record_only, dry_run,
11887 merge_options, ctx, pool, pool));
11890 SVN_WC__CALL_WITH_WRITE_LOCK(
11891 merge_peg_locked(&conflict_report,
11892 source_path_or_url, source_peg_revision,
11894 target_abspath, depth, ignore_mergeinfo,
11895 diff_ignore_ancestry,
11896 force_delete, record_only, dry_run,
11897 allow_mixed_rev, merge_options, ctx, pool, pool),
11898 ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
11900 SVN_ERR(merge_peg_locked(&conflict_report,
11901 source_path_or_url, source_peg_revision,
11903 target_abspath, depth, ignore_mergeinfo,
11904 diff_ignore_ancestry,
11905 force_delete, record_only, dry_run,
11906 allow_mixed_rev, merge_options, ctx, pool, pool));
11908 SVN_ERR(make_merge_conflict_error(conflict_report, pool));
11909 return SVN_NO_ERROR;
11913 /* The location-history of a branch.
11915 * This structure holds the set of path-revisions occupied by a branch,
11916 * from an externally chosen 'tip' location back to its origin. The
11917 * 'tip' location is the youngest location that we are considering on
11919 typedef struct branch_history_t
11921 /* The tip location of the branch. That is, the youngest location that's
11922 * in the repository and that we're considering. If we're considering a
11923 * target branch right up to an uncommitted WC, then this is the WC base
11924 * (pristine) location. */
11925 svn_client__pathrev_t *tip;
11926 /* The location-segment history, as mergeinfo. */
11927 svn_mergeinfo_t history;
11928 /* Whether the location-segment history reached as far as (necessarily
11929 the root path in) revision 0 -- a fact that can't be represented as
11931 svn_boolean_t has_r0_history;
11932 } branch_history_t;
11934 /* Return the location on BRANCH_HISTORY at revision REV, or NULL if none. */
11935 static svn_client__pathrev_t *
11936 location_on_branch_at_rev(const branch_history_t *branch_history,
11938 apr_pool_t *result_pool,
11939 apr_pool_t *scratch_pool)
11941 apr_hash_index_t *hi;
11943 for (hi = apr_hash_first(scratch_pool, branch_history->history); hi;
11944 hi = apr_hash_next(hi))
11946 const char *fspath = svn__apr_hash_index_key(hi);
11947 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
11950 for (i = 0; i < rangelist->nelts; i++)
11952 svn_merge_range_t *r = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
11953 if (r->start < rev && rev <= r->end)
11955 return svn_client__pathrev_create_with_relpath(
11956 branch_history->tip->repos_root_url,
11957 branch_history->tip->repos_uuid,
11958 rev, fspath + 1, result_pool);
11966 typedef struct source_and_target_t
11968 svn_client__pathrev_t *source;
11969 svn_ra_session_t *source_ra_session;
11970 branch_history_t source_branch;
11972 merge_target_t *target;
11973 svn_ra_session_t *target_ra_session;
11974 branch_history_t target_branch;
11976 /* Repos location of the youngest common ancestor of SOURCE and TARGET. */
11977 svn_client__pathrev_t *yca;
11978 } source_and_target_t;
11980 /* Set *INTERSECTION_P to the intersection of BRANCH_HISTORY with the
11981 * revision range OLDEST_REV to YOUNGEST_REV (inclusive).
11983 * If the intersection is empty, the result will be a branch history object
11984 * containing an empty (not null) history.
11986 * ### The 'tip' of the result is currently unchanged.
11988 static svn_error_t *
11989 branch_history_intersect_range(branch_history_t **intersection_p,
11990 const branch_history_t *branch_history,
11991 svn_revnum_t oldest_rev,
11992 svn_revnum_t youngest_rev,
11993 apr_pool_t *result_pool,
11994 apr_pool_t *scratch_pool)
11996 branch_history_t *result = apr_palloc(result_pool, sizeof(*result));
11998 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
11999 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
12000 SVN_ERR_ASSERT(oldest_rev >= 1);
12001 /* Allow a just-empty range (oldest = youngest + 1) but not an
12002 * arbitrary reverse range (such as oldest = youngest + 2). */
12003 SVN_ERR_ASSERT(oldest_rev <= youngest_rev + 1);
12005 if (oldest_rev <= youngest_rev)
12007 SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
12008 &result->history, branch_history->history,
12009 youngest_rev, oldest_rev - 1, TRUE /* include_range */,
12010 result_pool, scratch_pool));
12011 result->history = svn_mergeinfo_dup(result->history, result_pool);
12015 result->history = apr_hash_make(result_pool);
12017 result->has_r0_history = FALSE;
12019 /* ### TODO: Set RESULT->tip to the tip of the intersection. */
12020 result->tip = svn_client__pathrev_dup(branch_history->tip, result_pool);
12022 *intersection_p = result;
12023 return SVN_NO_ERROR;
12026 /* Set *OLDEST_P and *YOUNGEST_P to the oldest and youngest locations
12027 * (inclusive) along BRANCH. OLDEST_P and/or YOUNGEST_P may be NULL if not
12030 static svn_error_t *
12031 branch_history_get_endpoints(svn_client__pathrev_t **oldest_p,
12032 svn_client__pathrev_t **youngest_p,
12033 const branch_history_t *branch,
12034 apr_pool_t *result_pool,
12035 apr_pool_t *scratch_pool)
12037 svn_revnum_t youngest_rev, oldest_rev;
12039 SVN_ERR(svn_mergeinfo__get_range_endpoints(
12040 &youngest_rev, &oldest_rev,
12041 branch->history, scratch_pool));
12043 *oldest_p = location_on_branch_at_rev(
12044 branch, oldest_rev + 1, result_pool, scratch_pool);
12046 *youngest_p = location_on_branch_at_rev(
12047 branch, youngest_rev, result_pool, scratch_pool);
12048 return SVN_NO_ERROR;
12051 /* Implements the svn_log_entry_receiver_t interface.
12053 Set *BATON to LOG_ENTRY->revision and return SVN_ERR_CEASE_INVOCATION. */
12054 static svn_error_t *
12055 operative_rev_receiver(void *baton,
12056 svn_log_entry_t *log_entry,
12059 svn_revnum_t *operative_rev = baton;
12061 *operative_rev = log_entry->revision;
12063 /* We've found the youngest merged or oldest eligible revision, so
12066 ...but wait, shouldn't we care if LOG_ENTRY->NON_INHERITABLE is
12067 true? Because if it is, then LOG_ENTRY->REVISION is only
12068 partially merged/elgibile! And our only caller,
12069 find_last_merged_location (via short_circuit_mergeinfo_log) is
12070 interested in *fully* merged revisions. That's all true, but if
12071 find_last_merged_location() finds the youngest merged revision it
12072 will also check for the oldest eligible revision. So in the case
12073 the youngest merged rev is non-inheritable, the *same* non-inheritable
12074 rev will be found as the oldest eligible rev -- and
12075 find_last_merged_location() handles that situation. */
12076 return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
12079 /* Wrapper around svn_client__mergeinfo_log. All arguments are as per
12080 that private API. The discover_changed_paths, depth, and revprops args to
12081 svn_client__mergeinfo_log are always TRUE, svn_depth_infinity_t,
12082 and empty array respectively.
12084 If RECEIVER raises a SVN_ERR_CEASE_INVOCATION error, but still sets
12085 *REVISION to a valid revnum, then clear the error. Otherwise return
12087 static svn_error_t*
12088 short_circuit_mergeinfo_log(svn_mergeinfo_catalog_t *target_mergeinfo_cat,
12089 svn_boolean_t finding_merged,
12090 const char *target_path_or_url,
12091 const svn_opt_revision_t *target_peg_revision,
12092 const char *source_path_or_url,
12093 const svn_opt_revision_t *source_peg_revision,
12094 const svn_opt_revision_t *source_start_revision,
12095 const svn_opt_revision_t *source_end_revision,
12096 svn_log_entry_receiver_t receiver,
12097 svn_revnum_t *revision,
12098 svn_client_ctx_t *ctx,
12099 svn_ra_session_t *ra_session,
12100 apr_pool_t *result_pool,
12101 apr_pool_t *scratch_pool)
12103 apr_array_header_t *revprops;
12105 const char *session_url;
12107 SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
12109 revprops = apr_array_make(scratch_pool, 0, sizeof(const char *));
12110 err = svn_client__mergeinfo_log(finding_merged,
12111 target_path_or_url,
12112 target_peg_revision,
12113 target_mergeinfo_cat,
12114 source_path_or_url,
12115 source_peg_revision,
12116 source_start_revision,
12117 source_end_revision,
12118 receiver, revision,
12119 TRUE, svn_depth_infinity,
12120 revprops, ctx, ra_session,
12121 result_pool, scratch_pool);
12123 err = svn_error_compose_create(
12125 svn_ra_reparent(ra_session, session_url, scratch_pool));
12129 /* We expect RECEIVER to short-circuit the (potentially expensive) log
12130 by raising an SVN_ERR_CEASE_INVOCATION -- see operative_rev_receiver.
12131 So we can ignore that error, but only as long as we actually found a
12133 if (SVN_IS_VALID_REVNUM(*revision)
12134 && err->apr_err == SVN_ERR_CEASE_INVOCATION)
12136 svn_error_clear(err);
12141 return svn_error_trace(err);
12144 return SVN_NO_ERROR;
12147 /* Set *BASE_P to the last location on SOURCE_BRANCH such that all changes
12148 * on SOURCE_BRANCH after YCA up to and including *BASE_P have already
12149 * been fully merged into TARGET.
12152 * o-------o-----------o--- SOURCE_BRANCH
12156 * o-----------o----------- TARGET branch
12158 * In terms of mergeinfo:
12160 * Source a--... o=change, -=no-op revision
12162 * YCA --> o a---o---o---o---o--- d=delete, a=add-as-a-copy
12164 * Eligible -.eee.eeeeeeeeeeeeeeeeeeee .=not a source branch location
12166 * Tgt-mi -.mmm.mm-mm-------m------- m=merged to root of TARGET or
12167 * subtree of TARGET with no
12168 * operative changes outside of that
12169 * subtree, -=not merged
12171 * Eligible -.---.--e--eeeeeee-eeeeeee
12173 * Next --------^----------------- BASE is just before here.
12178 * o-----------o-------------
12180 * If no revisions from SOURCE_BRANCH have been completely merged to TARGET,
12181 * then set *BASE_P to the YCA.
12183 static svn_error_t *
12184 find_last_merged_location(svn_client__pathrev_t **base_p,
12185 svn_client__pathrev_t *yca,
12186 const branch_history_t *source_branch,
12187 svn_client__pathrev_t *target,
12188 svn_client_ctx_t *ctx,
12189 svn_ra_session_t *ra_session,
12190 apr_pool_t *result_pool,
12191 apr_pool_t *scratch_pool)
12193 svn_opt_revision_t source_peg_rev, source_start_rev, source_end_rev,
12195 svn_revnum_t youngest_merged_rev = SVN_INVALID_REVNUM;
12196 svn_mergeinfo_catalog_t target_mergeinfo_cat = NULL;
12198 source_peg_rev.kind = svn_opt_revision_number;
12199 source_peg_rev.value.number = source_branch->tip->rev;
12200 source_start_rev.kind = svn_opt_revision_number;
12201 source_start_rev.value.number = yca->rev;
12202 source_end_rev.kind = svn_opt_revision_number;
12203 source_end_rev.value.number = source_branch->tip->rev;
12204 target_opt_rev.kind = svn_opt_revision_number;
12205 target_opt_rev.value.number = target->rev;
12207 /* Find the youngest revision fully merged from SOURCE_BRANCH to TARGET,
12208 if such a revision exists. */
12209 SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
12210 TRUE, /* Find merged */
12211 target->url, &target_opt_rev,
12212 source_branch->tip->url,
12214 &source_end_rev, &source_start_rev,
12215 operative_rev_receiver,
12216 &youngest_merged_rev,
12218 result_pool, scratch_pool));
12220 if (!SVN_IS_VALID_REVNUM(youngest_merged_rev))
12222 /* No revisions have been completely merged from SOURCE_BRANCH to
12223 TARGET so the base for the next merge is the YCA. */
12228 /* One or more revisions have already been completely merged from
12229 SOURCE_BRANCH to TARGET, now find the oldest revision, older
12230 than the youngest merged revision, which is still eligible to
12231 be merged, if such exists. */
12232 branch_history_t *contiguous_source;
12233 svn_revnum_t base_rev;
12234 svn_revnum_t oldest_eligible_rev = SVN_INVALID_REVNUM;
12236 /* If the only revisions eligible are younger than the youngest merged
12237 revision we can simply assume that the youngest eligible revision
12238 is the youngest merged revision. Obviously this may not be true!
12239 The revisions between the youngest merged revision and the tip of
12240 the branch may have several inoperative revisions -- they may *all*
12241 be inoperative revisions! But for the purpose of this function
12242 (i.e. finding the youngest revision after the YCA where all revs have
12243 been merged) that doesn't matter. */
12244 source_end_rev.value.number = youngest_merged_rev;
12245 SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
12246 FALSE, /* Find eligible */
12247 target->url, &target_opt_rev,
12248 source_branch->tip->url,
12250 &source_start_rev, &source_end_rev,
12251 operative_rev_receiver,
12252 &oldest_eligible_rev,
12254 scratch_pool, scratch_pool));
12256 /* If there are revisions eligible for merging, use the oldest one
12257 to calculate the base. Otherwise there are no operative revisions
12258 to merge and we can simple set the base to the youngest revision
12260 if (SVN_IS_VALID_REVNUM(oldest_eligible_rev))
12261 base_rev = oldest_eligible_rev - 1;
12263 base_rev = youngest_merged_rev;
12265 /* Find the branch location just before the oldest eligible rev.
12266 (We can't just use the base revs calculated above because the branch
12267 might have a gap there.) */
12268 SVN_ERR(branch_history_intersect_range(&contiguous_source,
12269 source_branch, yca->rev,
12271 scratch_pool, scratch_pool));
12272 SVN_ERR(branch_history_get_endpoints(NULL, base_p, contiguous_source,
12273 result_pool, scratch_pool));
12276 return SVN_NO_ERROR;
12279 /* Find a merge base location on the target branch, like in a sync
12283 * o-------o-----------o---
12285 * -----o prev. \ \ this
12286 * YCA \ merge \ \ merge
12287 * o-----------o-----------o
12290 * Set *BASE_P to BASE, the youngest location in the history of S_T->source
12291 * (at or after the YCA) at which all revisions up to BASE are effectively
12292 * merged into S_T->target.
12294 * If no locations on the history of S_T->source are effectively merged to
12295 * S_T->target, set *BASE_P to the YCA.
12297 static svn_error_t *
12298 find_base_on_source(svn_client__pathrev_t **base_p,
12299 source_and_target_t *s_t,
12300 svn_client_ctx_t *ctx,
12301 apr_pool_t *result_pool,
12302 apr_pool_t *scratch_pool)
12304 SVN_ERR(find_last_merged_location(base_p,
12306 &s_t->source_branch,
12307 s_t->target_branch.tip,
12309 s_t->source_ra_session,
12310 result_pool, scratch_pool));
12311 return SVN_NO_ERROR;
12314 /* Find a merge base location on the target branch, like in a reintegrate
12318 * o-----------o-------o---
12320 * -----o merge / \ this
12322 * o-------o---------------o
12325 * Set *BASE_P to BASE, the youngest location in the history of S_T->target
12326 * (at or after the YCA) at which all revisions up to BASE are effectively
12327 * merged into S_T->source.
12329 * If no locations on the history of S_T->target are effectively merged to
12330 * S_T->source, set *BASE_P to the YCA.
12332 static svn_error_t *
12333 find_base_on_target(svn_client__pathrev_t **base_p,
12334 source_and_target_t *s_t,
12335 svn_client_ctx_t *ctx,
12336 apr_pool_t *result_pool,
12337 apr_pool_t *scratch_pool)
12339 SVN_ERR(find_last_merged_location(base_p,
12341 &s_t->target_branch,
12344 s_t->target_ra_session,
12345 result_pool, scratch_pool));
12347 return SVN_NO_ERROR;
12350 /* The body of client_find_automatic_merge(), which see.
12352 static svn_error_t *
12353 find_automatic_merge(svn_client__pathrev_t **base_p,
12354 svn_boolean_t *is_reintegrate_like,
12355 source_and_target_t *s_t,
12356 svn_client_ctx_t *ctx,
12357 apr_pool_t *result_pool,
12358 apr_pool_t *scratch_pool)
12360 svn_client__pathrev_t *base_on_source, *base_on_target;
12362 /* Get the location-history of each branch. */
12363 s_t->source_branch.tip = s_t->source;
12364 SVN_ERR(svn_client__get_history_as_mergeinfo(
12365 &s_t->source_branch.history, &s_t->source_branch.has_r0_history,
12366 s_t->source, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
12367 s_t->source_ra_session, ctx, scratch_pool));
12368 s_t->target_branch.tip = &s_t->target->loc;
12369 SVN_ERR(svn_client__get_history_as_mergeinfo(
12370 &s_t->target_branch.history, &s_t->target_branch.has_r0_history,
12371 &s_t->target->loc, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
12372 s_t->target_ra_session, ctx, scratch_pool));
12374 SVN_ERR(svn_client__calc_youngest_common_ancestor(
12375 &s_t->yca, s_t->source, s_t->source_branch.history,
12376 s_t->source_branch.has_r0_history,
12377 &s_t->target->loc, s_t->target_branch.history,
12378 s_t->target_branch.has_r0_history,
12379 result_pool, scratch_pool));
12382 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
12383 _("'%s@%ld' must be ancestrally related to "
12385 s_t->source->url, s_t->source->rev,
12386 s_t->target->loc.url, s_t->target->loc.rev);
12388 /* Find the latest revision of A synced to B and the latest
12389 * revision of B synced to A.
12391 * base_on_source = youngest_complete_synced_point(source, target)
12392 * base_on_target = youngest_complete_synced_point(target, source)
12394 SVN_ERR(find_base_on_source(&base_on_source, s_t,
12395 ctx, scratch_pool, scratch_pool));
12396 SVN_ERR(find_base_on_target(&base_on_target, s_t,
12397 ctx, scratch_pool, scratch_pool));
12399 /* Choose a base. */
12400 if (base_on_source->rev >= base_on_target->rev)
12402 *base_p = base_on_source;
12403 *is_reintegrate_like = FALSE;
12407 *base_p = base_on_target;
12408 *is_reintegrate_like = TRUE;
12411 return SVN_NO_ERROR;
12414 /** Find out what kind of automatic merge would be needed, when the target
12415 * is only known as a repository location rather than a WC.
12417 * Like find_automatic_merge() except that the target is
12418 * specified by @a target_path_or_url at @a target_revision, which must
12419 * refer to a repository location, instead of by a WC path argument.
12421 static svn_error_t *
12422 find_automatic_merge_no_wc(automatic_merge_t **merge_p,
12423 const char *source_path_or_url,
12424 const svn_opt_revision_t *source_revision,
12425 const char *target_path_or_url,
12426 const svn_opt_revision_t *target_revision,
12427 svn_client_ctx_t *ctx,
12428 apr_pool_t *result_pool,
12429 apr_pool_t *scratch_pool)
12431 source_and_target_t *s_t = apr_palloc(scratch_pool, sizeof(*s_t));
12432 svn_client__pathrev_t *target_loc;
12433 automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
12436 SVN_ERR(svn_client__ra_session_from_path2(
12437 &s_t->source_ra_session, &s_t->source,
12438 source_path_or_url, NULL, source_revision, source_revision,
12439 ctx, result_pool));
12442 SVN_ERR(svn_client__ra_session_from_path2(
12443 &s_t->target_ra_session, &target_loc,
12444 target_path_or_url, NULL, target_revision, target_revision,
12445 ctx, result_pool));
12446 s_t->target = apr_palloc(scratch_pool, sizeof(*s_t->target));
12447 s_t->target->abspath = NULL; /* indicate the target is not a WC */
12448 s_t->target->loc = *target_loc;
12450 SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
12451 ctx, result_pool, scratch_pool));
12453 merge->right = s_t->source;
12454 merge->target = &s_t->target->loc;
12455 merge->yca = s_t->yca;
12458 return SVN_NO_ERROR;
12461 /* Find the information needed to merge all unmerged changes from a source
12462 * branch into a target branch.
12464 * Set @a *merge_p to the information needed to merge all unmerged changes
12465 * (up to @a source_revision) from the source branch @a source_path_or_url
12466 * at @a source_revision into the target WC at @a target_abspath.
12468 * The flags @a allow_mixed_rev, @a allow_local_mods and
12469 * @a allow_switched_subtrees enable merging into a WC that is in any or all
12470 * of the states described by their names, but only if this function decides
12471 * that the merge will be in the same direction as the last automatic merge.
12472 * If, on the other hand, the last automatic merge was in the opposite
12473 * direction, then such states of the WC are not allowed regardless
12474 * of these flags. This function merely records these flags in the
12475 * @a *merge_p structure; do_automatic_merge_locked() checks the WC
12476 * state for compliance.
12478 * Allocate the @a *merge_p structure in @a result_pool.
12480 static svn_error_t *
12481 client_find_automatic_merge(automatic_merge_t **merge_p,
12482 const char *source_path_or_url,
12483 const svn_opt_revision_t *source_revision,
12484 const char *target_abspath,
12485 svn_boolean_t allow_mixed_rev,
12486 svn_boolean_t allow_local_mods,
12487 svn_boolean_t allow_switched_subtrees,
12488 svn_client_ctx_t *ctx,
12489 apr_pool_t *result_pool,
12490 apr_pool_t *scratch_pool)
12492 source_and_target_t *s_t = apr_palloc(result_pool, sizeof(*s_t));
12493 automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
12495 /* "Open" the target WC. Check the target WC for mixed-rev, local mods and
12496 * switched subtrees yet to faster exit and notify user before contacting
12497 * with server. After we find out what kind of merge is required, then if a
12498 * reintegrate-like merge is required we'll do the stricter checks, in
12499 * do_automatic_merge_locked(). */
12500 SVN_ERR(open_target_wc(&s_t->target, target_abspath,
12503 allow_switched_subtrees,
12504 ctx, result_pool, scratch_pool));
12506 /* Open RA sessions to the source and target trees. */
12507 SVN_ERR(svn_client_open_ra_session2(&s_t->target_ra_session,
12508 s_t->target->loc.url,
12509 s_t->target->abspath,
12510 ctx, result_pool, scratch_pool));
12511 /* ### check for null URL (i.e. added path) here, like in reintegrate? */
12512 SVN_ERR(svn_client__ra_session_from_path2(
12513 &s_t->source_ra_session, &s_t->source,
12514 source_path_or_url, NULL, source_revision, source_revision,
12515 ctx, result_pool));
12517 /* Check source is in same repos as target. */
12518 SVN_ERR(check_same_repos(s_t->source, source_path_or_url,
12519 &s_t->target->loc, target_abspath,
12520 TRUE /* strict_urls */, scratch_pool));
12522 SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
12523 ctx, result_pool, scratch_pool));
12524 merge->yca = s_t->yca;
12525 merge->right = s_t->source;
12526 merge->allow_mixed_rev = allow_mixed_rev;
12527 merge->allow_local_mods = allow_local_mods;
12528 merge->allow_switched_subtrees = allow_switched_subtrees;
12532 /* TODO: Close the source and target sessions here? */
12534 return SVN_NO_ERROR;
12537 /* Perform an automatic merge, given the information in MERGE which
12538 * must have come from calling client_find_automatic_merge().
12540 * Four locations are inputs: YCA, BASE, RIGHT, TARGET, as shown
12541 * depending on whether the base is on the source branch or the target
12542 * branch of this merge.
12544 * RIGHT (is_reintegrate_like)
12545 * o-----------o-------o---
12547 * -----o merge / \ this
12549 * o-------o---------------o
12554 * BASE RIGHT (! is_reintegrate_like)
12555 * o-------o-----------o---
12557 * -----o prev. \ \ this
12558 * YCA \ merge \ \ merge
12559 * o-----------o-----------o
12562 * ### TODO: The reintegrate-like code path does not yet
12563 * eliminate already-cherry-picked revisions from the source.
12565 static svn_error_t *
12566 do_automatic_merge_locked(conflict_report_t **conflict_report,
12567 const automatic_merge_t *merge,
12568 const char *target_abspath,
12570 svn_boolean_t diff_ignore_ancestry,
12571 svn_boolean_t force_delete,
12572 svn_boolean_t record_only,
12573 svn_boolean_t dry_run,
12574 const apr_array_header_t *merge_options,
12575 svn_client_ctx_t *ctx,
12576 apr_pool_t *result_pool,
12577 apr_pool_t *scratch_pool)
12579 merge_target_t *target;
12580 svn_boolean_t reintegrate_like = merge->is_reintegrate_like;
12581 svn_boolean_t use_sleep = FALSE;
12584 SVN_ERR(open_target_wc(&target, target_abspath,
12585 merge->allow_mixed_rev && ! reintegrate_like,
12586 merge->allow_local_mods && ! reintegrate_like,
12587 merge->allow_switched_subtrees && ! reintegrate_like,
12588 ctx, scratch_pool, scratch_pool));
12590 if (reintegrate_like)
12592 merge_source_t source;
12593 svn_ra_session_t *base_ra_session = NULL;
12594 svn_ra_session_t *right_ra_session = NULL;
12595 svn_ra_session_t *target_ra_session = NULL;
12598 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12599 _("The required merge is reintegrate-like, "
12600 "and the record-only option "
12601 "cannot be used with this kind of merge"));
12603 if (depth != svn_depth_unknown)
12604 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12605 _("The required merge is reintegrate-like, "
12606 "and the depth option "
12607 "cannot be used with this kind of merge"));
12610 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12611 _("The required merge is reintegrate-like, "
12612 "and the force_delete option "
12613 "cannot be used with this kind of merge"));
12615 SVN_ERR(ensure_ra_session_url(&base_ra_session, merge->base->url,
12616 target->abspath, ctx, scratch_pool));
12617 SVN_ERR(ensure_ra_session_url(&right_ra_session, merge->right->url,
12618 target->abspath, ctx, scratch_pool));
12619 SVN_ERR(ensure_ra_session_url(&target_ra_session, target->loc.url,
12620 target->abspath, ctx, scratch_pool));
12622 /* Check for and reject any abnormalities -- such as revisions that
12623 * have not yet been merged in the opposite direction -- that a
12624 * 'reintegrate' merge would have rejected. */
12626 merge_source_t *source2;
12628 SVN_ERR(find_reintegrate_merge(&source2, NULL,
12629 right_ra_session, merge->right,
12630 target_ra_session, target,
12631 ctx, scratch_pool, scratch_pool));
12634 source.loc1 = merge->base;
12635 source.loc2 = merge->right;
12636 source.ancestral = ! merge->is_reintegrate_like;
12638 err = merge_cousins_and_supplement_mergeinfo(conflict_report,
12643 &source, merge->yca,
12644 TRUE /* same_repos */,
12646 FALSE /*diff_ignore_ancestry*/,
12647 force_delete, record_only,
12651 result_pool, scratch_pool);
12653 else /* ! merge->is_reintegrate_like */
12655 /* Ignoring the base that we found, we pass the YCA instead and let
12656 do_merge() work out which subtrees need which revision ranges to
12657 be merged. This enables do_merge() to fill in revision-range
12658 gaps that are older than the base that we calculated (which is
12659 for the root path of the merge).
12661 An improvement would be to change find_automatic_merge() to
12662 find the base for each sutree, and then here use the oldest base
12663 among all subtrees. */
12664 apr_array_header_t *merge_sources;
12665 svn_ra_session_t *ra_session = NULL;
12667 /* Normalize our merge sources, do_merge() requires this. See the
12668 'MERGEINFO MERGE SOURCE NORMALIZATION' global comment. */
12669 SVN_ERR(ensure_ra_session_url(&ra_session, merge->right->url,
12670 target->abspath, ctx, scratch_pool));
12671 SVN_ERR(normalize_merge_sources_internal(
12672 &merge_sources, merge->right,
12673 svn_rangelist__initialize(merge->yca->rev, merge->right->rev, TRUE,
12675 ra_session, ctx, scratch_pool, scratch_pool));
12677 err = do_merge(NULL, NULL, conflict_report, &use_sleep,
12678 merge_sources, target, ra_session,
12679 TRUE /*related*/, TRUE /*same_repos*/,
12680 FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
12681 force_delete, dry_run,
12682 record_only, NULL, FALSE, FALSE, depth, merge_options,
12683 ctx, result_pool, scratch_pool);
12687 svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
12691 return SVN_NO_ERROR;
12695 svn_client_get_merging_summary(svn_boolean_t *needs_reintegration,
12696 const char **yca_url, svn_revnum_t *yca_rev,
12697 const char **base_url, svn_revnum_t *base_rev,
12698 const char **right_url, svn_revnum_t *right_rev,
12699 const char **target_url, svn_revnum_t *target_rev,
12700 const char **repos_root_url,
12701 const char *source_path_or_url,
12702 const svn_opt_revision_t *source_revision,
12703 const char *target_path_or_url,
12704 const svn_opt_revision_t *target_revision,
12705 svn_client_ctx_t *ctx,
12706 apr_pool_t *result_pool,
12707 apr_pool_t *scratch_pool)
12709 svn_boolean_t target_is_wc;
12710 automatic_merge_t *merge;
12712 target_is_wc = (! svn_path_is_url(target_path_or_url))
12713 && (target_revision->kind == svn_opt_revision_unspecified
12714 || target_revision->kind == svn_opt_revision_working);
12716 SVN_ERR(client_find_automatic_merge(
12718 source_path_or_url, source_revision,
12719 target_path_or_url,
12720 TRUE, TRUE, TRUE, /* allow_* */
12721 ctx, scratch_pool, scratch_pool));
12723 SVN_ERR(find_automatic_merge_no_wc(
12725 source_path_or_url, source_revision,
12726 target_path_or_url, target_revision,
12727 ctx, scratch_pool, scratch_pool));
12729 if (needs_reintegration)
12730 *needs_reintegration = merge->is_reintegrate_like;
12732 *yca_url = apr_pstrdup(result_pool, merge->yca->url);
12734 *yca_rev = merge->yca->rev;
12736 *base_url = apr_pstrdup(result_pool, merge->base->url);
12738 *base_rev = merge->base->rev;
12740 *right_url = apr_pstrdup(result_pool, merge->right->url);
12742 *right_rev = merge->right->rev;
12744 *target_url = apr_pstrdup(result_pool, merge->target->url);
12746 *target_rev = merge->target->rev;
12747 if (repos_root_url)
12748 *repos_root_url = apr_pstrdup(result_pool, merge->yca->repos_root_url);
12750 return SVN_NO_ERROR;