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_fspath.h"
58 #include "private/svn_mergeinfo_private.h"
59 #include "private/svn_client_private.h"
60 #include "private/svn_sorts_private.h"
61 #include "private/svn_subr_private.h"
62 #include "private/svn_wc_private.h"
64 #include "svn_private_config.h"
67 /*-----------------------------------------------------------------------*/
69 /* MERGEINFO MERGE SOURCE NORMALIZATION
71 * Nearly any helper function herein that accepts two URL/revision
72 * pairs (or equivalent struct merge_source_t) expects one of two things
75 * 1. that mergeinfo is not being recorded at all for this
78 * 2. that the pairs represent two locations along a single line
79 * of version history such that there are no copies in the
80 * history of the object between the locations when treating
81 * the oldest of the two locations as non-inclusive. In other
82 * words, if there is a copy at all between them, there is only
83 * one copy and its source was the oldest of the two locations.
85 * We use svn_ra_get_location_segments() to split a given range of
86 * revisions across an object's history into several which obey these
87 * rules. For example, an extract from the log of Subversion's own
88 * /subversion/tags/1.4.5 directory shows the following copies between
89 * r859500 and r866500 (omitting the '/subversion' prefix for clarity):
92 * A /branches/1.4.x (from /trunk:859597)
95 * A /tags/1.4.4 (from /branches/1.4.x:865262)
96 * # Notice that this copy leaves a gap between 865262 and 865417.
99 * A /branches/1.4.5 (from /tags/1.4.4:866419)
103 * A /tags/1.4.5 (from /branches/1.4.5:866424)
107 * 859500 859597 865262 866419 866424 866500
109 * trunk ------------------------------------------------
111 * branches/1.4.x A-------------------------------------
114 * tags/1.4.4 . A-----------------------
116 * branches/1.4.5 . . A------D
118 * tags/1.4.5 . . . A---------
120 * 859598 865417 866420 866425
122 * A merge of the difference between r859500 and r866500 of this directory
123 * gets split into sequential merges of the following location pairs.
125 * 859500 859597 865262 865416 866419 866424 866500
127 * trunk (======] . . . . .
130 * branches/1.4.x ======] . . . .
132 * branches/1.4.x ( . . . .
133 * tags/1.4.4 =============] . .
134 * implicit_src_gap (======] . . .
137 * branches/1.4.5 ======] .
142 * which are represented in merge_source_t as:
144 * [/trunk:859500, /trunk:859597]
145 * (recorded in svn:mergeinfo as /trunk:859501-859597)
147 * [/trunk:859597, /branches/1.4.x:865262]
148 * (recorded in svn:mergeinfo as /branches/1.4.x:859598-865262)
150 * [/branches/1.4.x:865262, /tags/1.4.4@866419]
151 * (recorded in svn:mergeinfo as /tags/1.4.4:865263-866419)
152 * (and there is a gap, the revision range [865262, 865416])
154 * [/tags/1.4.4@866419, /branches/1.4.5@866424]
155 * (recorded in svn:mergeinfo as /branches/1.4.5:866420-866424)
157 * [/branches/1.4.5@866424, /tags/1.4.5@866500]
158 * (recorded in svn:mergeinfo as /tags/1.4.5:866425-866500)
160 * Our helper functions would then operate on one of these location
164 /* WHICH SVN_CLIENT_MERGE* API DO I WANT?
166 * libsvn_client has three public merge APIs; they are all wrappers
167 * around the do_merge engine. Which one to use depends on the number
168 * of URLs passed as arguments and whether or not specific merge
169 * ranges (-c/-r) are specified.
172 * +----+--------------------------------+---------------------+
173 * | -c | mergeinfo-driven | |
174 * | or | cherrypicking | |
175 * | -r | (svn_client_merge_peg) | |
176 * |----+--------------------------------+ |
177 * | | mergeinfo-driven | unsupported |
178 * | | 'cherry harvest', i.e. merge | |
179 * | | all revisions from URL that | |
180 * | no | have not already been merged | |
181 * | -c | (svn_client_merge_peg) | |
182 * | or +--------------------------------+---------------------+
183 * | -r | mergeinfo-driven | mergeinfo-writing |
184 * | | whole-branch | diff-and-apply |
185 * | | heuristic merge | (svn_client_merge) |
186 * | | (svn_client_merge_reintegrate) | |
187 * +----+--------------------------------+---------------------+
192 /* THE CHILDREN_WITH_MERGEINFO ARRAY
194 * Many of the helper functions in this file pass around an
195 * apr_array_header_t *CHILDREN_WITH_MERGEINFO. This is a depth first
196 * sorted array filled with svn_client__merge_path_t * describing the
197 * merge target and any of its subtrees which have explicit mergeinfo
198 * or otherwise need special attention during a merge.
200 * During mergeinfo unaware merges, CHILDREN_WITH_MERGEINFO contains
201 * contains only one element (added by do_mergeinfo_unaware_dir_merge)
202 * describing a contiguous range to be merged to the WC merge target.
204 * During mergeinfo aware merges CHILDREN_WITH_MERGEINFO is created
205 * by get_mergeinfo_paths() and outside of that function and its helpers
206 * should always meet the criteria dictated in get_mergeinfo_paths()'s doc
207 * string. The elements of CHILDREN_WITH_MERGEINFO should never be NULL.
209 * For clarification on mergeinfo aware vs. mergeinfo unaware merges, see
210 * the doc string for HONOR_MERGEINFO().
214 /*-----------------------------------------------------------------------*/
216 /*** Repos-Diff Editor Callbacks ***/
219 typedef struct merge_source_t
221 /* "left" side URL and revision (inclusive iff youngest) */
222 const svn_client__pathrev_t *loc1;
224 /* "right" side URL and revision (inclusive iff youngest) */
225 const svn_client__pathrev_t *loc2;
227 /* True iff LOC1 is an ancestor of LOC2 or vice-versa (history-wise). */
228 svn_boolean_t ancestral;
231 /* Description of the merge target root node (a WC working node) */
232 typedef struct merge_target_t
234 /* Absolute path to the WC node */
237 /* The repository location of the base node of the target WC. If the node
238 * is locally added, then URL & REV are NULL & SVN_INVALID_REVNUM.
239 * REPOS_ROOT_URL and REPOS_UUID are always valid. */
240 svn_client__pathrev_t loc;
244 typedef struct merge_cmd_baton_t {
245 svn_boolean_t force_delete; /* Delete a file/dir even if modified */
246 svn_boolean_t dry_run;
247 svn_boolean_t record_only; /* Whether to merge only mergeinfo
249 svn_boolean_t same_repos; /* Whether the merge source repository
250 is the same repository as the
251 target. Defaults to FALSE if DRY_RUN
253 svn_boolean_t mergeinfo_capable; /* Whether the merge source server
254 is capable of Merge Tracking. */
255 svn_boolean_t ignore_mergeinfo; /* Don't honor mergeinfo; see
256 doc string of do_merge(). FALSE if
257 MERGE_SOURCE->ancestral is FALSE. */
258 svn_boolean_t diff_ignore_ancestry; /* Diff unrelated nodes as if related; see
259 doc string of do_merge(). FALSE if
260 MERGE_SOURCE->ancestral is FALSE. */
261 svn_boolean_t reintegrate_merge; /* Whether this is a --reintegrate
263 const merge_target_t *target; /* Description of merge target node */
265 /* The left and right URLs and revs. The value of this field changes to
266 reflect the merge_source_t *currently* being merged by do_merge(). */
267 merge_source_t merge_source;
269 /* Rangelist containing single range which describes the gap, if any,
270 in the natural history of the merge source currently being processed.
271 See http://subversion.tigris.org/issues/show_bug.cgi?id=3432.
272 Updated during each call to do_directory_merge(). May be NULL if there
274 svn_rangelist_t *implicit_src_gap;
276 svn_client_ctx_t *ctx; /* Client context for callbacks, etc. */
278 /* The list of any paths which remained in conflict after a
279 resolution attempt was made. We track this in-memory, rather
280 than just using WC entry state, since the latter doesn't help us
281 when in dry_run mode.
282 ### And because we only want to resolve conflicts that were
283 generated by this merge, not pre-existing ones? */
284 apr_hash_t *conflicted_paths;
286 /* A list of absolute paths which had no explicit mergeinfo prior to the
287 merge but got explicit mergeinfo added by the merge. This is populated
288 by merge_change_props() and is allocated in POOL so it is subject to the
289 lifetime limitations of POOL. Is NULL if no paths are found which
290 meet the criteria or DRY_RUN is true. */
291 apr_hash_t *paths_with_new_mergeinfo;
293 /* A list of absolute paths whose mergeinfo doesn't need updating after
294 the merge. This can be caused by the removal of mergeinfo by the merge
295 or by deleting the node itself. This is populated by merge_change_props()
296 and the delete callbacks and is allocated in POOL so it is subject to the
297 lifetime limitations of POOL. Is NULL if no paths are found which
298 meet the criteria or DRY_RUN is true. */
299 apr_hash_t *paths_with_deleted_mergeinfo;
301 /* The list of absolute skipped paths, which should be examined and
302 cleared after each invocation of the callback. The paths
303 are absolute. Is NULL if MERGE_B->MERGE_SOURCE->ancestral and
304 MERGE_B->REINTEGRATE_MERGE are both false. */
305 apr_hash_t *skipped_abspaths;
307 /* The list of absolute merged paths. Unused if MERGE_B->MERGE_SOURCE->ancestral
308 and MERGE_B->REINTEGRATE_MERGE are both false. */
309 apr_hash_t *merged_abspaths;
311 /* A hash of (const char *) absolute WC paths mapped to the same which
312 represent the roots of subtrees added by the merge. */
313 apr_hash_t *added_abspaths;
315 /* A list of tree conflict victim absolute paths which may be NULL. */
316 apr_hash_t *tree_conflicted_abspaths;
318 /* The diff3_cmd in ctx->config, if any, else null. We could just
319 extract this as needed, but since more than one caller uses it,
320 we just set it up when this baton is created. */
321 const char *diff3_cmd;
322 const apr_array_header_t *merge_options;
324 /* Array of file extension patterns to preserve as extensions in
325 generated conflict files. */
326 const apr_array_header_t *ext_patterns;
328 /* RA sessions used throughout a merge operation. Opened/re-parented
331 NOTE: During the actual merge editor drive, RA_SESSION1 is used
332 for the primary editing and RA_SESSION2 for fetching additional
333 information -- as necessary -- from the repository. So during
334 this phase of the merge, you *must not* reparent RA_SESSION1; use
335 (temporarily reparenting if you must) RA_SESSION2 instead. */
336 svn_ra_session_t *ra_session1;
337 svn_ra_session_t *ra_session2;
339 /* During the merge, *USE_SLEEP is set to TRUE if a sleep will be required
340 afterwards to ensure timestamp integrity, or unchanged if not. */
341 svn_boolean_t *use_sleep;
343 /* Pool which has a lifetime limited to one iteration over a given
344 merge source, i.e. it is cleared on every call to do_directory_merge()
345 or do_file_merge() in do_merge(). */
349 /* State for notify_merge_begin() */
350 struct notify_begin_state_t
352 /* Cache of which abspath was last notified. */
353 const char *last_abspath;
355 /* Reference to the one-and-only CHILDREN_WITH_MERGEINFO (see global
356 comment) or a similar list for single-file-merges */
357 const apr_array_header_t *nodes_with_mergeinfo;
363 /* Return TRUE iff we should be taking account of mergeinfo in deciding what
364 changes to merge, for the merge described by MERGE_B. Specifically, that
365 is if the merge source server is capable of merge tracking, the left-side
366 merge source is an ancestor of the right-side (or vice-versa), the merge
367 source is in the same repository as the merge target, and we are not
368 ignoring mergeinfo. */
369 #define HONOR_MERGEINFO(merge_b) ((merge_b)->mergeinfo_capable \
370 && (merge_b)->merge_source.ancestral \
371 && (merge_b)->same_repos \
372 && (! (merge_b)->ignore_mergeinfo))
375 /* Return TRUE iff we should be recording mergeinfo for the merge described
376 by MERGE_B. Specifically, that is if we are honoring mergeinfo and the
377 merge is not a dry run. */
378 #define RECORD_MERGEINFO(merge_b) (HONOR_MERGEINFO(merge_b) \
379 && !(merge_b)->dry_run)
382 /*-----------------------------------------------------------------------*/
386 /* Return TRUE iff the session URL of RA_SESSION is equal to URL. Useful in
387 * asserting preconditions. */
389 session_url_is(svn_ra_session_t *ra_session,
391 apr_pool_t *scratch_pool)
393 const char *session_url;
395 = svn_ra_get_session_url(ra_session, &session_url, scratch_pool);
397 SVN_ERR_ASSERT_NO_RETURN(! err);
398 return strcmp(url, session_url) == 0;
401 /* Return a new merge_source_t structure, allocated in RESULT_POOL,
402 * initialized with deep copies of LOC1 and LOC2 and ANCESTRAL. */
403 static merge_source_t *
404 merge_source_create(const svn_client__pathrev_t *loc1,
405 const svn_client__pathrev_t *loc2,
406 svn_boolean_t ancestral,
407 apr_pool_t *result_pool)
410 = apr_palloc(result_pool, sizeof(*s));
412 s->loc1 = svn_client__pathrev_dup(loc1, result_pool);
413 s->loc2 = svn_client__pathrev_dup(loc2, result_pool);
414 s->ancestral = ancestral;
418 /* Return a deep copy of SOURCE, allocated in RESULT_POOL. */
419 static merge_source_t *
420 merge_source_dup(const merge_source_t *source,
421 apr_pool_t *result_pool)
423 merge_source_t *s = apr_palloc(result_pool, sizeof(*s));
425 s->loc1 = svn_client__pathrev_dup(source->loc1, result_pool);
426 s->loc2 = svn_client__pathrev_dup(source->loc2, result_pool);
427 s->ancestral = source->ancestral;
431 /* Return SVN_ERR_UNSUPPORTED_FEATURE if URL is not inside the repository
432 of LOCAL_ABSPATH. Use SCRATCH_POOL for temporary allocations. */
434 check_repos_match(const merge_target_t *target,
435 const char *local_abspath,
437 apr_pool_t *scratch_pool)
439 if (!svn_uri__is_ancestor(target->loc.repos_root_url, url))
440 return svn_error_createf(
441 SVN_ERR_UNSUPPORTED_FEATURE, NULL,
442 _("URL '%s' of '%s' is not in repository '%s'"),
443 url, svn_dirent_local_style(local_abspath, scratch_pool),
444 target->loc.repos_root_url);
449 /* Return TRUE iff the repository of LOCATION1 is the same as
450 * that of LOCATION2. If STRICT_URLS is true, the URLs must
451 * match (and the UUIDs, just to be sure), otherwise just the UUIDs must
452 * match and the URLs can differ (a common case is http versus https). */
454 is_same_repos(const svn_client__pathrev_t *location1,
455 const svn_client__pathrev_t *location2,
456 svn_boolean_t strict_urls)
459 return (strcmp(location1->repos_root_url, location2->repos_root_url) == 0
460 && strcmp(location1->repos_uuid, location2->repos_uuid) == 0);
462 return (strcmp(location1->repos_uuid, location2->repos_uuid) == 0);
465 /* If the repository identified of LOCATION1 is not the same as that
466 * of LOCATION2, throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES
467 * error mentioning PATH1 and PATH2. For STRICT_URLS, see is_same_repos().
470 check_same_repos(const svn_client__pathrev_t *location1,
472 const svn_client__pathrev_t *location2,
474 svn_boolean_t strict_urls,
475 apr_pool_t *scratch_pool)
477 if (! is_same_repos(location1, location2, strict_urls))
478 return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
479 _("'%s' must be from the same repository as "
480 "'%s'"), path1, path2);
484 /* Store LOCAL_ABSPATH in PATH_HASH after duplicating it into the pool
485 containing PATH_HASH. */
486 static APR_INLINE void
487 store_path(apr_hash_t *path_hash, const char *local_abspath)
489 const char *dup_path = apr_pstrdup(apr_hash_pool_get(path_hash),
492 svn_hash_sets(path_hash, dup_path, dup_path);
495 /* Store LOCAL_ABSPATH in *PATH_HASH_P after duplicating it into the pool
496 containing *PATH_HASH_P. If *PATH_HASH_P is NULL, then first set
497 *PATH_HASH_P to a new hash allocated from POOL. */
498 static APR_INLINE void
499 alloc_and_store_path(apr_hash_t **path_hash_p,
500 const char *local_abspath,
504 *path_hash_p = apr_hash_make(pool);
505 store_path(*path_hash_p, local_abspath);
508 /* Return whether any WC path was put in conflict by the merge
509 operation corresponding to MERGE_B. */
510 static APR_INLINE svn_boolean_t
511 is_path_conflicted_by_merge(merge_cmd_baton_t *merge_b)
513 return (merge_b->conflicted_paths &&
514 apr_hash_count(merge_b->conflicted_paths) > 0);
517 /* Return a state indicating whether the WC metadata matches the
518 * node kind on disk of the local path LOCAL_ABSPATH.
519 * Use MERGE_B to determine the dry-run details; particularly, if a dry run
520 * noted that it deleted this path, assume matching node kinds (as if both
521 * kinds were svn_node_none).
523 * - Return svn_wc_notify_state_inapplicable if the node kind matches.
524 * - Return 'obstructed' if there is a node on disk where none or a
525 * different kind is expected, or if the disk node cannot be read.
526 * - Return 'missing' if there is no node on disk but one is expected.
527 * Also return 'missing' for server-excluded nodes (not here due to
528 * authz or other reasons determined by the server).
530 * Optionally return a bit more info for interested users.
533 perform_obstruction_check(svn_wc_notify_state_t *obstruction_state,
534 svn_boolean_t *deleted,
535 svn_boolean_t *excluded,
536 svn_node_kind_t *kind,
537 svn_depth_t *parent_depth,
538 const merge_cmd_baton_t *merge_b,
539 const char *local_abspath,
540 apr_pool_t *scratch_pool)
542 svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
543 svn_node_kind_t wc_kind;
544 svn_boolean_t check_root;
546 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
548 *obstruction_state = svn_wc_notify_state_inapplicable;
553 *kind = svn_node_none;
558 check_root = ! strcmp(local_abspath, merge_b->target->abspath);
560 SVN_ERR(svn_wc__check_for_obstructions(obstruction_state,
565 wc_ctx, local_abspath,
571 /* Create *LEFT and *RIGHT conflict versions for conflict victim
572 * at VICTIM_ABSPATH, with merge-left node kind MERGE_LEFT_NODE_KIND
573 * and merge-right node kind MERGE_RIGHT_NODE_KIND, using information
574 * obtained 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 merge_left_node_kind,
581 svn_node_kind_t merge_right_node_kind,
582 const merge_source_t *merge_source,
583 const merge_target_t *target,
584 apr_pool_t *result_pool,
585 apr_pool_t *scratch_pool)
587 const char *child = svn_dirent_skip_ancestor(target->abspath,
589 const char *left_relpath, *right_relpath;
591 SVN_ERR_ASSERT(child != NULL);
592 left_relpath = svn_client__pathrev_relpath(merge_source->loc1,
594 right_relpath = svn_client__pathrev_relpath(merge_source->loc2,
597 *left = svn_wc_conflict_version_create2(
598 merge_source->loc1->repos_root_url,
599 merge_source->loc1->repos_uuid,
600 svn_relpath_join(left_relpath, child, scratch_pool),
601 merge_source->loc1->rev,
602 merge_left_node_kind, result_pool);
604 *right = svn_wc_conflict_version_create2(
605 merge_source->loc2->repos_root_url,
606 merge_source->loc2->repos_uuid,
607 svn_relpath_join(right_relpath, child, scratch_pool),
608 merge_source->loc2->rev,
609 merge_right_node_kind, result_pool);
614 /* Helper for filter_self_referential_mergeinfo()
616 *MERGEINFO is a non-empty, non-null collection of mergeinfo.
618 Remove all mergeinfo from *MERGEINFO that describes revision ranges
619 greater than REVISION. Put a copy of any removed mergeinfo, allocated
620 in POOL, into *YOUNGER_MERGEINFO.
622 If no mergeinfo is removed from *MERGEINFO then *YOUNGER_MERGEINFO is set
623 to NULL. If all mergeinfo is removed from *MERGEINFO then *MERGEINFO is
627 split_mergeinfo_on_revision(svn_mergeinfo_t *younger_mergeinfo,
628 svn_mergeinfo_t *mergeinfo,
629 svn_revnum_t revision,
632 apr_hash_index_t *hi;
633 apr_pool_t *iterpool = svn_pool_create(pool);
635 *younger_mergeinfo = NULL;
636 for (hi = apr_hash_first(pool, *mergeinfo); hi; hi = apr_hash_next(hi))
639 const char *merge_source_path = apr_hash_this_key(hi);
640 svn_rangelist_t *rangelist = apr_hash_this_val(hi);
642 svn_pool_clear(iterpool);
644 for (i = 0; i < rangelist->nelts; i++)
646 svn_merge_range_t *range =
647 APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
648 if (range->end <= revision)
650 /* This entirely of this range is as old or older than
651 REVISION, so leave it in *MERGEINFO. */
656 /* Since the rangelists in svn_mergeinfo_t's are sorted in
657 increasing order we know that part or all of *this* range
658 and *all* of the remaining ranges in *RANGELIST are younger
659 than REVISION. Remove the younger rangelists from
660 *MERGEINFO and put them in *YOUNGER_MERGEINFO. */
662 svn_rangelist_t *younger_rangelist =
663 apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
665 for (j = i; j < rangelist->nelts; j++)
667 svn_merge_range_t *younger_range = svn_merge_range_dup(
668 APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *), pool);
670 /* REVISION might intersect with the first range where
671 range->end > REVISION. If that is the case then split
672 the current range into two, putting the younger half
673 into *YOUNGER_MERGEINFO and leaving the older half in
675 if (j == i && range->start + 1 <= revision)
676 younger_range->start = range->end = revision;
678 APR_ARRAY_PUSH(younger_rangelist, svn_merge_range_t *) =
682 /* So far we've only been manipulating rangelists, now we
683 actually create *YOUNGER_MERGEINFO and then remove the older
684 ranges from *MERGEINFO */
685 if (!(*younger_mergeinfo))
686 *younger_mergeinfo = apr_hash_make(pool);
687 svn_hash_sets(*younger_mergeinfo, merge_source_path,
689 SVN_ERR(svn_mergeinfo_remove2(mergeinfo, *younger_mergeinfo,
690 *mergeinfo, TRUE, pool, iterpool));
691 break; /* ...out of for (i = 0; i < rangelist->nelts; i++) */
696 svn_pool_destroy(iterpool);
702 /* Make a copy of PROPCHANGES (array of svn_prop_t) into *TRIMMED_PROPCHANGES,
703 omitting any svn:mergeinfo changes. */
705 omit_mergeinfo_changes(apr_array_header_t **trimmed_propchanges,
706 const apr_array_header_t *propchanges,
707 apr_pool_t *result_pool)
711 *trimmed_propchanges = apr_array_make(result_pool,
715 for (i = 0; i < propchanges->nelts; ++i)
717 const svn_prop_t *change = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
719 /* If this property is not svn:mergeinfo, then copy it. */
720 if (strcmp(change->name, SVN_PROP_MERGEINFO) != 0)
721 APR_ARRAY_PUSH(*trimmed_propchanges, svn_prop_t) = *change;
728 /* Helper for merge_props_changed().
730 *PROPS is an array of svn_prop_t structures representing regular properties
731 to be added to the working copy TARGET_ABSPATH.
733 The merge source and target are assumed to be in the same repository.
735 Filter out mergeinfo property additions to TARGET_ABSPATH when
736 those additions refer to the same line of history as TARGET_ABSPATH as
739 Examine the added mergeinfo, looking at each range (or single rev)
740 of each source path. If a source_path/range refers to the same line of
741 history as TARGET_ABSPATH (pegged at its base revision), then filter out
742 that range. If the entire rangelist for a given path is filtered then
743 filter out the path as well.
745 RA_SESSION is an open RA session to the repository
746 in which both the source and target live, else RA_SESSION is not used. It
747 may be temporarily reparented as needed by this function.
749 Use CTX for any further client operations.
751 If any filtering occurs, set outgoing *PROPS to a shallow copy (allocated
752 in POOL) of incoming *PROPS minus the filtered mergeinfo. */
754 filter_self_referential_mergeinfo(apr_array_header_t **props,
755 const char *target_abspath,
756 svn_ra_session_t *ra_session,
757 svn_client_ctx_t *ctx,
760 apr_array_header_t *adjusted_props;
762 apr_pool_t *iterpool;
763 svn_boolean_t is_copy;
764 const char *repos_relpath;
765 svn_client__pathrev_t target_base;
767 /* If PATH itself has been added there is no need to filter. */
768 SVN_ERR(svn_wc__node_get_origin(&is_copy, &target_base.rev, &repos_relpath,
769 &target_base.repos_root_url,
770 &target_base.repos_uuid, NULL, NULL,
771 ctx->wc_ctx, target_abspath, FALSE,
774 if (is_copy || !repos_relpath)
775 return SVN_NO_ERROR; /* A copy or a local addition */
777 target_base.url = svn_path_url_add_component2(target_base.repos_root_url,
778 repos_relpath, pool);
780 adjusted_props = apr_array_make(pool, (*props)->nelts, sizeof(svn_prop_t));
781 iterpool = svn_pool_create(pool);
782 for (i = 0; i < (*props)->nelts; ++i)
784 svn_prop_t *prop = &APR_ARRAY_IDX((*props), i, svn_prop_t);
786 svn_mergeinfo_t mergeinfo, younger_mergeinfo;
787 svn_mergeinfo_t filtered_mergeinfo = NULL;
788 svn_mergeinfo_t filtered_younger_mergeinfo = NULL;
791 /* If this property isn't mergeinfo or is NULL valued (i.e. prop removal)
792 or empty mergeinfo it does not require any special handling. There
793 is nothing to filter out of empty mergeinfo and the concept of
794 filtering doesn't apply if we are trying to remove mergeinfo
796 if ((strcmp(prop->name, SVN_PROP_MERGEINFO) != 0)
797 || (! prop->value) /* Removal of mergeinfo */
798 || (! prop->value->len)) /* Empty mergeinfo */
800 APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
804 svn_pool_clear(iterpool);
806 /* Non-empty mergeinfo; filter self-referential mergeinfo out. */
808 /* Parse the incoming mergeinfo to allow easier manipulation. */
809 err = svn_mergeinfo_parse(&mergeinfo, prop->value->data, iterpool);
813 /* Issue #3896: If we can't parse it, we certainly can't
815 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
817 svn_error_clear(err);
818 APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
823 return svn_error_trace(err);
827 /* The working copy target PATH is at BASE_REVISION. Divide the
828 incoming mergeinfo into two groups. One where all revision ranges
829 are as old or older than BASE_REVISION and one where all revision
832 Note: You may be wondering why we do this.
834 For the incoming mergeinfo "older" than target's base revision we
835 can filter out self-referential mergeinfo efficiently using
836 svn_client__get_history_as_mergeinfo(). We simply look at PATH's
837 natural history as mergeinfo and remove that from any incoming
840 For mergeinfo "younger" than the base revision we can't use
841 svn_ra_get_location_segments() to look into PATH's future
842 history. Instead we must use svn_client__repos_locations() and
843 look at each incoming source/range individually and see if PATH
844 at its base revision and PATH at the start of the incoming range
845 exist on the same line of history. If they do then we can filter
846 out the incoming range. But since we have to do this for each
847 range there is a substantial performance penalty to pay if the
848 incoming ranges are not contiguous, i.e. we call
849 svn_client__repos_locations for each discrete range and incur
850 the cost of a roundtrip communication with the repository. */
851 SVN_ERR(split_mergeinfo_on_revision(&younger_mergeinfo,
856 /* Filter self-referential mergeinfo from younger_mergeinfo. */
857 if (younger_mergeinfo)
859 apr_hash_index_t *hi;
860 const char *merge_source_root_url;
862 SVN_ERR(svn_ra_get_repos_root2(ra_session,
863 &merge_source_root_url, iterpool));
865 for (hi = apr_hash_first(iterpool, younger_mergeinfo);
866 hi; hi = apr_hash_next(hi))
869 const char *source_path = apr_hash_this_key(hi);
870 svn_rangelist_t *rangelist = apr_hash_this_val(hi);
871 const char *merge_source_url;
872 svn_rangelist_t *adjusted_rangelist =
873 apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
876 svn_path_url_add_component2(merge_source_root_url,
877 source_path + 1, iterpool);
879 for (j = 0; j < rangelist->nelts; j++)
882 svn_client__pathrev_t *start_loc;
883 svn_merge_range_t *range =
884 APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *);
886 /* Because the merge source normalization code
887 ensures mergeinfo refers to real locations on
888 the same line of history, there's no need to
889 look at the whole range, just the start. */
891 /* Check if PATH@BASE_REVISION exists at
892 RANGE->START on the same line of history.
893 (start+1 because RANGE->start is not inclusive.) */
894 err2 = svn_client__repos_location(&start_loc, ra_session,
897 ctx, iterpool, iterpool);
900 if (err2->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES
901 || err2->apr_err == SVN_ERR_FS_NOT_FOUND
902 || err2->apr_err == SVN_ERR_FS_NO_SUCH_REVISION)
904 /* PATH@BASE_REVISION didn't exist at
905 RANGE->START + 1 or is unrelated to the
906 resource PATH@RANGE->START. Some of the
907 requested revisions may not even exist in
908 the repository; a real possibility since
909 mergeinfo is hand editable. In all of these
910 cases clear and ignore the error and don't
913 Note: In this last case it is possible that
914 we will allow self-referential mergeinfo to
915 be applied, but fixing it here is potentially
916 very costly in terms of finding what part of
917 a range is actually valid. Simply allowing
918 the merge to proceed without filtering the
919 offending range seems the least worst
921 svn_error_clear(err2);
923 APR_ARRAY_PUSH(adjusted_rangelist,
924 svn_merge_range_t *) = range;
928 return svn_error_trace(err2);
933 /* PATH@BASE_REVISION exists on the same
934 line of history at RANGE->START and RANGE->END.
935 Now check that PATH@BASE_REVISION's path
936 names at RANGE->START and RANGE->END are the same.
937 If the names are not the same then the mergeinfo
938 describing PATH@RANGE->START through
939 PATH@RANGE->END actually belong to some other
940 line of history and we want to record this
941 mergeinfo, not filter it. */
942 if (strcmp(start_loc->url, merge_source_url) != 0)
944 APR_ARRAY_PUSH(adjusted_rangelist,
945 svn_merge_range_t *) = range;
948 /* else no need to add, this mergeinfo is
949 all on the same line of history. */
950 } /* for (j = 0; j < rangelist->nelts; j++) */
952 /* Add any rangelists for source_path that are not
954 if (adjusted_rangelist->nelts)
956 if (!filtered_younger_mergeinfo)
957 filtered_younger_mergeinfo = apr_hash_make(iterpool);
958 svn_hash_sets(filtered_younger_mergeinfo, source_path,
962 } /* Iteration over each merge source in younger_mergeinfo. */
963 } /* if (younger_mergeinfo) */
965 /* Filter self-referential mergeinfo from "older" mergeinfo. */
968 svn_mergeinfo_t implicit_mergeinfo;
970 SVN_ERR(svn_client__get_history_as_mergeinfo(
971 &implicit_mergeinfo, NULL,
972 &target_base, target_base.rev, SVN_INVALID_REVNUM,
973 ra_session, ctx, iterpool));
975 /* Remove PATH's implicit mergeinfo from the incoming mergeinfo. */
976 SVN_ERR(svn_mergeinfo_remove2(&filtered_mergeinfo,
978 mergeinfo, TRUE, iterpool, iterpool));
981 /* Combine whatever older and younger filtered mergeinfo exists
982 into filtered_mergeinfo. */
983 if (filtered_mergeinfo && filtered_younger_mergeinfo)
984 SVN_ERR(svn_mergeinfo_merge2(filtered_mergeinfo,
985 filtered_younger_mergeinfo, iterpool,
987 else if (filtered_younger_mergeinfo)
988 filtered_mergeinfo = filtered_younger_mergeinfo;
990 /* If there is any incoming mergeinfo remaining after filtering
991 then put it in adjusted_props. */
992 if (filtered_mergeinfo && apr_hash_count(filtered_mergeinfo))
994 /* Convert filtered_mergeinfo to a svn_prop_t and put it
995 back in the array. */
996 svn_string_t *filtered_mergeinfo_str;
997 svn_prop_t *adjusted_prop = apr_pcalloc(pool,
998 sizeof(*adjusted_prop));
999 SVN_ERR(svn_mergeinfo_to_string(&filtered_mergeinfo_str,
1002 adjusted_prop->name = SVN_PROP_MERGEINFO;
1003 adjusted_prop->value = filtered_mergeinfo_str;
1004 APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *adjusted_prop;
1007 svn_pool_destroy(iterpool);
1009 *props = adjusted_props;
1010 return SVN_NO_ERROR;
1013 /* Prepare a set of property changes PROPCHANGES to be used for a merge
1014 operation on LOCAL_ABSPATH.
1016 Remove all non-regular prop-changes (entry-props and WC-props).
1017 Remove all non-mergeinfo prop-changes if it's a record-only merge.
1018 Remove self-referential mergeinfo (### in some cases...)
1019 Remove foreign-repository mergeinfo (### in some cases...)
1021 Store the resulting property changes in *PROP_UPDATES.
1022 Store information on where mergeinfo is updated in MERGE_B.
1024 Used for both file and directory property merges. */
1025 static svn_error_t *
1026 prepare_merge_props_changed(const apr_array_header_t **prop_updates,
1027 const char *local_abspath,
1028 const apr_array_header_t *propchanges,
1029 merge_cmd_baton_t *merge_b,
1030 apr_pool_t *result_pool,
1031 apr_pool_t *scratch_pool)
1033 apr_array_header_t *props;
1035 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1037 /* We only want to merge "regular" version properties: by
1038 definition, 'svn merge' shouldn't touch any data within .svn/ */
1039 SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
1042 /* If we are only applying mergeinfo changes then we need to do
1043 additional filtering of PROPS so it contains only mergeinfo changes. */
1044 if (merge_b->record_only && props->nelts)
1046 apr_array_header_t *mergeinfo_props =
1047 apr_array_make(result_pool, 1, sizeof(svn_prop_t));
1050 for (i = 0; i < props->nelts; i++)
1052 svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
1054 if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
1056 APR_ARRAY_PUSH(mergeinfo_props, svn_prop_t) = *prop;
1060 props = mergeinfo_props;
1065 /* Issue #3383: We don't want mergeinfo from a foreign repos.
1067 If this is a merge from a foreign repository we must strip all
1068 incoming mergeinfo (including mergeinfo deletions). */
1069 if (! merge_b->same_repos)
1070 SVN_ERR(omit_mergeinfo_changes(&props, props, result_pool));
1072 /* If this is a forward merge then don't add new mergeinfo to
1073 PATH that is already part of PATH's own history, see
1074 http://svn.haxx.se/dev/archive-2008-09/0006.shtml. If the
1075 merge sources are not ancestral then there is no concept of a
1076 'forward' or 'reverse' merge and we filter unconditionally. */
1077 if (merge_b->merge_source.loc1->rev < merge_b->merge_source.loc2->rev
1078 || !merge_b->merge_source.ancestral)
1080 if (HONOR_MERGEINFO(merge_b) || merge_b->reintegrate_merge)
1081 SVN_ERR(filter_self_referential_mergeinfo(&props,
1083 merge_b->ra_session2,
1088 *prop_updates = props;
1090 /* Make a record in BATON if we find a PATH where mergeinfo is added
1091 where none existed previously or PATH is having its existing
1092 mergeinfo deleted. */
1097 for (i = 0; i < props->nelts; ++i)
1099 svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
1101 if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
1103 /* Does LOCAL_ABSPATH have any pristine mergeinfo? */
1104 svn_boolean_t has_pristine_mergeinfo = FALSE;
1105 apr_hash_t *pristine_props;
1107 SVN_ERR(svn_wc_get_pristine_props(&pristine_props,
1108 merge_b->ctx->wc_ctx,
1114 && svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
1115 has_pristine_mergeinfo = TRUE;
1117 if (!has_pristine_mergeinfo && prop->value)
1119 alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
1120 local_abspath, merge_b->pool);
1122 else if (has_pristine_mergeinfo && !prop->value)
1124 alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
1125 local_abspath, merge_b->pool);
1131 return SVN_NO_ERROR;
1134 #define CONFLICT_REASON_NONE ((svn_wc_conflict_reason_t)-1)
1135 #define CONFLICT_REASON_SKIP ((svn_wc_conflict_reason_t)-2)
1136 #define CONFLICT_REASON_SKIP_WC ((svn_wc_conflict_reason_t)-3)
1138 /* Baton used for testing trees for being editted while performing tree
1139 conflict detection for incoming deletes */
1140 struct dir_delete_baton_t
1142 /* Reference to dir baton of directory that is the root of the deletion */
1143 struct merge_dir_baton_t *del_root;
1145 /* Boolean indicating that some edit is found. Allows avoiding more work */
1146 svn_boolean_t found_edit;
1148 /* A list of paths that are compared. Kept up to date until FOUND_EDIT is
1150 apr_hash_t *compared_abspaths;
1153 /* Baton for the merge_dir_*() functions. Initialized in merge_dir_opened() */
1154 struct merge_dir_baton_t
1156 /* Reference to the parent baton, unless the parent is the anchor, in which
1157 case PARENT_BATON is NULL */
1158 struct merge_dir_baton_t *parent_baton;
1160 /* The pool containing this baton. Use for RESULT_POOL for storing in this
1164 /* This directory doesn't have a representation in the working copy, so any
1165 operation on it will be skipped and possibly cause a tree conflict on the
1167 svn_boolean_t shadowed;
1169 /* This node or one of its descendants received operational changes from the
1170 merge. If this node is the shadow root its tree conflict status has been
1172 svn_boolean_t edited;
1174 /* If a tree conflict will be installed once edited, it's reason. If a skip
1175 should be produced its reason. Otherwise CONFLICT_REASON_NONE for no tree
1179 CONFLICT_REASON_SKIP:
1180 The node will be skipped with content and property state as stored in
1183 CONFLICT_REASON_SKIP_WC:
1184 The node will be skipped as an obstructing working copy.
1186 svn_wc_conflict_reason_t tree_conflict_reason;
1187 svn_wc_conflict_action_t tree_conflict_action;
1188 svn_node_kind_t tree_conflict_local_node_kind;
1189 svn_node_kind_t tree_conflict_merge_left_node_kind;
1190 svn_node_kind_t tree_conflict_merge_right_node_kind;
1192 /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
1193 add to the notification */
1194 svn_wc_notify_state_t skip_reason;
1196 /* TRUE if the node was added by this merge. Otherwise FALSE */
1197 svn_boolean_t added;
1198 svn_boolean_t add_is_replace; /* Add is second part of replace */
1200 /* TRUE if we are taking over an existing directory as addition, otherwise
1202 svn_boolean_t add_existing;
1204 /* NULL, or an hashtable mapping const char * local_abspaths to
1205 const char *kind mapping, containing deleted nodes that still need a delete
1206 notification (which may be a replaced notification if the node is not just
1208 apr_hash_t *pending_deletes;
1210 /* NULL, or an hashtable mapping const char * LOCAL_ABSPATHs to
1211 a const svn_wc_conflict_description2_t * instance, describing the just
1212 installed conflict */
1213 apr_hash_t *new_tree_conflicts;
1215 /* If not NULL, a reference to the information of the delete test that is
1216 currently in progress. Allocated in the root-directory baton, referenced
1217 from all descendants */
1218 struct dir_delete_baton_t *delete_state;
1221 /* Baton for the merge_dir_*() functions. Initialized in merge_file_opened() */
1222 struct merge_file_baton_t
1224 /* Reference to the parent baton, unless the parent is the anchor, in which
1225 case PARENT_BATON is NULL */
1226 struct merge_dir_baton_t *parent_baton;
1228 /* This file doesn't have a representation in the working copy, so any
1229 operation on it will be skipped and possibly cause a tree conflict
1230 on the shadow root */
1231 svn_boolean_t shadowed;
1233 /* This node received operational changes from the merge. If this node
1234 is the shadow root its tree conflict status has been applied */
1235 svn_boolean_t edited;
1237 /* If a tree conflict will be installed once edited, it's reason. If a skip
1238 should be produced its reason. Some special values are defined. See the
1239 merge_tree_baton_t for an explanation. */
1240 svn_wc_conflict_reason_t tree_conflict_reason;
1241 svn_wc_conflict_action_t tree_conflict_action;
1242 svn_node_kind_t tree_conflict_local_node_kind;
1243 svn_node_kind_t tree_conflict_merge_left_node_kind;
1244 svn_node_kind_t tree_conflict_merge_right_node_kind;
1246 /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
1247 add to the notification */
1248 svn_wc_notify_state_t skip_reason;
1250 /* TRUE if the node was added by this merge. Otherwise FALSE */
1251 svn_boolean_t added;
1252 svn_boolean_t add_is_replace; /* Add is second part of replace */
1255 /* Forward declaration */
1256 static svn_error_t *
1257 notify_merge_begin(merge_cmd_baton_t *merge_b,
1258 const char *local_abspath,
1259 svn_boolean_t delete_action,
1260 apr_pool_t *scratch_pool);
1262 /* Record the skip for future processing and (later) produce the
1263 skip notification */
1264 static svn_error_t *
1265 record_skip(merge_cmd_baton_t *merge_b,
1266 const char *local_abspath,
1267 svn_node_kind_t kind,
1268 svn_wc_notify_action_t action,
1269 svn_wc_notify_state_t state,
1270 struct merge_dir_baton_t *pdb,
1271 apr_pool_t *scratch_pool)
1273 if (merge_b->record_only)
1274 return SVN_NO_ERROR; /* ### Why? - Legacy compatibility */
1276 if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
1277 && !(pdb && pdb->shadowed))
1279 store_path(merge_b->skipped_abspaths, local_abspath);
1282 if (merge_b->ctx->notify_func2)
1284 svn_wc_notify_t *notify;
1286 SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
1288 notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
1289 notify->kind = kind;
1290 notify->content_state = notify->prop_state = state;
1292 merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
1295 return SVN_NO_ERROR;
1298 /* Forward declaration */
1299 static svn_client__merge_path_t *
1300 find_nearest_ancestor_with_intersecting_ranges(
1301 svn_revnum_t *start,
1303 const apr_array_header_t *children_with_mergeinfo,
1304 svn_boolean_t path_is_own_ancestor,
1305 const char *local_abspath);
1307 /* Record a tree conflict in the WC, unless this is a dry run or a record-
1308 * only merge, or if a tree conflict is already flagged for the VICTIM_PATH.
1309 * (The latter can happen if a merge-tracking-aware merge is doing multiple
1310 * editor drives because of a gap in the range of eligible revisions.)
1312 * The tree conflict, with its victim specified by VICTIM_PATH, is
1313 * assumed to have happened during a merge using merge baton MERGE_B.
1315 * ACTION and REASON correspond to the fields
1316 * of the same names in svn_wc_tree_conflict_description_t.
1318 static svn_error_t *
1319 record_tree_conflict(merge_cmd_baton_t *merge_b,
1320 const char *local_abspath,
1321 struct merge_dir_baton_t *parent_baton,
1322 svn_node_kind_t local_node_kind,
1323 svn_node_kind_t merge_left_node_kind,
1324 svn_node_kind_t merge_right_node_kind,
1325 svn_wc_conflict_action_t action,
1326 svn_wc_conflict_reason_t reason,
1327 const svn_wc_conflict_description2_t *existing_conflict,
1328 svn_boolean_t notify_tc,
1329 apr_pool_t *scratch_pool)
1331 svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
1333 if (merge_b->record_only)
1334 return SVN_NO_ERROR;
1336 if (merge_b->merge_source.ancestral
1337 || merge_b->reintegrate_merge)
1339 store_path(merge_b->tree_conflicted_abspaths, local_abspath);
1342 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
1345 if (!merge_b->dry_run)
1347 svn_wc_conflict_description2_t *conflict;
1348 const svn_wc_conflict_version_t *left;
1349 const svn_wc_conflict_version_t *right;
1350 apr_pool_t *result_pool = parent_baton ? parent_baton->pool
1353 if (reason == svn_wc_conflict_reason_deleted)
1355 const char *moved_to_abspath;
1357 SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
1358 wc_ctx, local_abspath,
1359 scratch_pool, scratch_pool));
1361 if (moved_to_abspath)
1363 /* Local abspath itself has been moved away. If only a
1364 descendant is moved away, we call the node itself deleted */
1365 reason = svn_wc_conflict_reason_moved_away;
1368 else if (reason == svn_wc_conflict_reason_added)
1370 const char *moved_from_abspath;
1371 SVN_ERR(svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
1372 wc_ctx, local_abspath,
1373 scratch_pool, scratch_pool));
1374 if (moved_from_abspath)
1375 reason = svn_wc_conflict_reason_moved_here;
1378 if (HONOR_MERGEINFO(merge_b) && merge_b->merge_source.ancestral)
1380 struct merge_source_t *source;
1381 svn_client__pathrev_t *loc1;
1382 svn_client__pathrev_t *loc2;
1383 svn_merge_range_t range =
1384 {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE};
1386 /* We are honoring mergeinfo so do not blindly record
1387 * a conflict describing the merge of
1388 * SOURCE->LOC1->URL@SOURCE->LOC1->REV through
1389 * SOURCE->LOC2->URL@SOURCE->LOC2->REV
1390 * but figure out the actual revision range merged. */
1391 (void)find_nearest_ancestor_with_intersecting_ranges(
1392 &(range.start), &(range.end),
1393 merge_b->notify_begin.nodes_with_mergeinfo,
1394 action != svn_wc_conflict_action_delete,
1396 loc1 = svn_client__pathrev_dup(merge_b->merge_source.loc1,
1398 loc2 = svn_client__pathrev_dup(merge_b->merge_source.loc2,
1400 loc1->rev = range.start;
1401 loc2->rev = range.end;
1402 source = merge_source_create(loc1, loc2,
1403 merge_b->merge_source.ancestral,
1405 SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
1406 merge_left_node_kind,
1407 merge_right_node_kind,
1408 source, merge_b->target,
1409 result_pool, scratch_pool));
1412 SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
1413 merge_left_node_kind,
1414 merge_right_node_kind,
1415 &merge_b->merge_source, merge_b->target,
1416 result_pool, scratch_pool));
1418 /* Fix up delete of file, add of dir replacement (or other way around) */
1419 if (existing_conflict != NULL && existing_conflict->src_left_version)
1420 left = existing_conflict->src_left_version;
1422 conflict = svn_wc_conflict_description_create_tree2(
1423 local_abspath, local_node_kind,
1424 svn_wc_operation_merge,
1425 left, right, result_pool);
1427 conflict->action = action;
1428 conflict->reason = reason;
1430 /* May return SVN_ERR_WC_PATH_UNEXPECTED_STATUS */
1431 if (existing_conflict)
1432 SVN_ERR(svn_wc__del_tree_conflict(wc_ctx, local_abspath,
1435 SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
1440 if (! parent_baton->new_tree_conflicts)
1441 parent_baton->new_tree_conflicts = apr_hash_make(result_pool);
1443 svn_hash_sets(parent_baton->new_tree_conflicts,
1444 apr_pstrdup(result_pool, local_abspath),
1448 /* ### TODO: Store in parent baton */
1451 /* On a replacement we currently get two tree conflicts */
1452 if (merge_b->ctx->notify_func2 && notify_tc)
1454 svn_wc_notify_t *notify;
1456 SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
1458 notify = svn_wc_create_notify(local_abspath, svn_wc_notify_tree_conflict,
1460 notify->kind = local_node_kind;
1462 merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
1466 return SVN_NO_ERROR;
1469 /* Record the add for future processing and produce the
1470 update_add notification
1472 static svn_error_t *
1473 record_update_add(merge_cmd_baton_t *merge_b,
1474 const char *local_abspath,
1475 svn_node_kind_t kind,
1476 svn_boolean_t notify_replaced,
1477 apr_pool_t *scratch_pool)
1479 if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
1481 store_path(merge_b->merged_abspaths, local_abspath);
1484 if (merge_b->ctx->notify_func2)
1486 svn_wc_notify_t *notify;
1487 svn_wc_notify_action_t action = svn_wc_notify_update_add;
1489 SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
1491 if (notify_replaced)
1492 action = svn_wc_notify_update_replace;
1494 notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
1495 notify->kind = kind;
1497 merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
1501 return SVN_NO_ERROR;
1504 /* Record the update for future processing and produce the
1505 update_update notification */
1506 static svn_error_t *
1507 record_update_update(merge_cmd_baton_t *merge_b,
1508 const char *local_abspath,
1509 svn_node_kind_t kind,
1510 svn_wc_notify_state_t content_state,
1511 svn_wc_notify_state_t prop_state,
1512 apr_pool_t *scratch_pool)
1514 if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
1516 store_path(merge_b->merged_abspaths, local_abspath);
1519 if (merge_b->ctx->notify_func2)
1521 svn_wc_notify_t *notify;
1523 SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
1525 notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_update,
1527 notify->kind = kind;
1528 notify->content_state = content_state;
1529 notify->prop_state = prop_state;
1531 merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
1535 return SVN_NO_ERROR;
1538 /* Record the delete for future processing and for (later) producing the
1539 update_delete notification */
1540 static svn_error_t *
1541 record_update_delete(merge_cmd_baton_t *merge_b,
1542 struct merge_dir_baton_t *parent_db,
1543 const char *local_abspath,
1544 svn_node_kind_t kind,
1545 apr_pool_t *scratch_pool)
1547 /* Update the lists of merged, skipped, tree-conflicted and added paths. */
1548 if (merge_b->merge_source.ancestral
1549 || merge_b->reintegrate_merge)
1551 /* Issue #4166: If a previous merge added NOTIFY_ABSPATH, but we
1552 are now deleting it, then remove it from the list of added
1554 svn_hash_sets(merge_b->added_abspaths, local_abspath, NULL);
1555 store_path(merge_b->merged_abspaths, local_abspath);
1558 SVN_ERR(notify_merge_begin(merge_b, local_abspath, TRUE, scratch_pool));
1562 const char *dup_abspath = apr_pstrdup(parent_db->pool, local_abspath);
1564 if (!parent_db->pending_deletes)
1565 parent_db->pending_deletes = apr_hash_make(parent_db->pool);
1567 svn_hash_sets(parent_db->pending_deletes, dup_abspath,
1568 svn_node_kind_to_word(kind));
1571 return SVN_NO_ERROR;
1574 /* Notify the pending 'D'eletes, that were waiting to see if a matching 'A'dd
1575 might make them a 'R'eplace. */
1576 static svn_error_t *
1577 handle_pending_notifications(merge_cmd_baton_t *merge_b,
1578 struct merge_dir_baton_t *db,
1579 apr_pool_t *scratch_pool)
1581 if (merge_b->ctx->notify_func2 && db->pending_deletes)
1583 apr_hash_index_t *hi;
1585 for (hi = apr_hash_first(scratch_pool, db->pending_deletes);
1587 hi = apr_hash_next(hi))
1589 const char *del_abspath = apr_hash_this_key(hi);
1590 svn_wc_notify_t *notify;
1592 notify = svn_wc_create_notify(del_abspath,
1593 svn_wc_notify_update_delete,
1595 notify->kind = svn_node_kind_from_word(
1596 apr_hash_this_val(hi));
1598 merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2,
1599 notify, scratch_pool);
1602 db->pending_deletes = NULL;
1604 return SVN_NO_ERROR;
1607 /* Helper function for the merge_dir_*() and merge_file_*() functions.
1609 Installs and notifies pre-recorded tree conflicts and skips for
1610 ancestors of operational merges
1612 static svn_error_t *
1613 mark_dir_edited(merge_cmd_baton_t *merge_b,
1614 struct merge_dir_baton_t *db,
1615 const char *local_abspath,
1616 apr_pool_t *scratch_pool)
1618 /* ### Too much common code with mark_file_edited */
1620 return SVN_NO_ERROR;
1622 if (db->parent_baton && !db->parent_baton->edited)
1624 const char *dir_abspath = svn_dirent_dirname(local_abspath,
1627 SVN_ERR(mark_dir_edited(merge_b, db->parent_baton, dir_abspath,
1634 return SVN_NO_ERROR; /* Easy out */
1636 if (db->parent_baton
1637 && db->parent_baton->delete_state
1638 && db->tree_conflict_reason != CONFLICT_REASON_NONE)
1640 db->parent_baton->delete_state->found_edit = TRUE;
1642 else if (db->tree_conflict_reason == CONFLICT_REASON_SKIP
1643 || db->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
1645 /* open_directory() decided not to flag a tree conflict, but
1646 for clarity we produce a skip for this node that
1647 most likely isn't touched by the merge itself */
1649 if (merge_b->ctx->notify_func2)
1651 svn_wc_notify_t *notify;
1653 SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE,
1656 notify = svn_wc_create_notify(
1658 (db->tree_conflict_reason == CONFLICT_REASON_SKIP)
1659 ? svn_wc_notify_skip
1660 : svn_wc_notify_update_skip_obstruction,
1662 notify->kind = svn_node_dir;
1663 notify->content_state = notify->prop_state = db->skip_reason;
1665 merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2,
1670 if (merge_b->merge_source.ancestral
1671 || merge_b->reintegrate_merge)
1673 store_path(merge_b->skipped_abspaths, local_abspath);
1676 else if (db->tree_conflict_reason != CONFLICT_REASON_NONE)
1678 /* open_directory() decided that a tree conflict should be raised */
1680 SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
1681 db->tree_conflict_local_node_kind,
1682 db->tree_conflict_merge_left_node_kind,
1683 db->tree_conflict_merge_right_node_kind,
1684 db->tree_conflict_action,
1685 db->tree_conflict_reason,
1690 return SVN_NO_ERROR;
1693 /* Helper function for the merge_file_*() functions.
1695 Installs and notifies pre-recorded tree conflicts and skips for
1696 ancestors of operational merges
1698 static svn_error_t *
1699 mark_file_edited(merge_cmd_baton_t *merge_b,
1700 struct merge_file_baton_t *fb,
1701 const char *local_abspath,
1702 apr_pool_t *scratch_pool)
1704 /* ### Too much common code with mark_dir_edited */
1706 return SVN_NO_ERROR;
1708 if (fb->parent_baton && !fb->parent_baton->edited)
1710 const char *dir_abspath = svn_dirent_dirname(local_abspath,
1713 SVN_ERR(mark_dir_edited(merge_b, fb->parent_baton, dir_abspath,
1720 return SVN_NO_ERROR; /* Easy out */
1722 if (fb->parent_baton
1723 && fb->parent_baton->delete_state
1724 && fb->tree_conflict_reason != CONFLICT_REASON_NONE)
1726 fb->parent_baton->delete_state->found_edit = TRUE;
1728 else if (fb->tree_conflict_reason == CONFLICT_REASON_SKIP
1729 || fb->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
1731 /* open_directory() decided not to flag a tree conflict, but
1732 for clarity we produce a skip for this node that
1733 most likely isn't touched by the merge itself */
1735 if (merge_b->ctx->notify_func2)
1737 svn_wc_notify_t *notify;
1739 SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE,
1742 notify = svn_wc_create_notify(local_abspath, svn_wc_notify_skip,
1744 notify->kind = svn_node_file;
1745 notify->content_state = notify->prop_state = fb->skip_reason;
1747 merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2,
1752 if (merge_b->merge_source.ancestral
1753 || merge_b->reintegrate_merge)
1755 store_path(merge_b->skipped_abspaths, local_abspath);
1758 else if (fb->tree_conflict_reason != CONFLICT_REASON_NONE)
1760 /* open_file() decided that a tree conflict should be raised */
1762 SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
1763 fb->tree_conflict_local_node_kind,
1764 fb->tree_conflict_merge_left_node_kind,
1765 fb->tree_conflict_merge_right_node_kind,
1766 fb->tree_conflict_action,
1767 fb->tree_conflict_reason,
1772 return SVN_NO_ERROR;
1775 /* An svn_diff_tree_processor_t function.
1777 Called before either merge_file_changed(), merge_file_added(),
1778 merge_file_deleted() or merge_file_closed(), unless it sets *SKIP to TRUE.
1780 When *SKIP is TRUE, the diff driver avoids work on getting the details
1781 for the closing callbacks.
1783 static svn_error_t *
1784 merge_file_opened(void **new_file_baton,
1785 svn_boolean_t *skip,
1786 const char *relpath,
1787 const svn_diff_source_t *left_source,
1788 const svn_diff_source_t *right_source,
1789 const svn_diff_source_t *copyfrom_source,
1791 const struct svn_diff_tree_processor_t *processor,
1792 apr_pool_t *result_pool,
1793 apr_pool_t *scratch_pool)
1795 merge_cmd_baton_t *merge_b = processor->baton;
1796 struct merge_dir_baton_t *pdb = dir_baton;
1797 struct merge_file_baton_t *fb;
1798 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
1799 relpath, scratch_pool);
1801 fb = apr_pcalloc(result_pool, sizeof(*fb));
1802 fb->tree_conflict_reason = CONFLICT_REASON_NONE;
1803 fb->tree_conflict_action = svn_wc_conflict_action_edit;
1804 fb->skip_reason = svn_wc_notify_state_unknown;
1807 fb->tree_conflict_merge_left_node_kind = svn_node_file;
1809 fb->tree_conflict_merge_left_node_kind = svn_node_none;
1812 fb->tree_conflict_merge_right_node_kind = svn_node_file;
1814 fb->tree_conflict_merge_right_node_kind = svn_node_none;
1816 *new_file_baton = fb;
1820 fb->parent_baton = pdb;
1821 fb->shadowed = pdb->shadowed;
1822 fb->skip_reason = pdb->skip_reason;
1827 /* An ancestor is tree conflicted. Nothing to do here. */
1829 else if (left_source != NULL)
1831 /* Node is expected to be a file, which will be changed or deleted. */
1832 svn_boolean_t is_deleted;
1833 svn_boolean_t excluded;
1834 svn_depth_t parent_depth;
1837 fb->tree_conflict_action = svn_wc_conflict_action_delete;
1840 svn_wc_notify_state_t obstr_state;
1842 SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
1843 &fb->tree_conflict_local_node_kind,
1845 merge_b, local_abspath,
1848 if (obstr_state != svn_wc_notify_state_inapplicable)
1850 fb->shadowed = TRUE;
1851 fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1852 fb->skip_reason = obstr_state;
1853 return SVN_NO_ERROR;
1857 fb->tree_conflict_local_node_kind = svn_node_none;
1860 if (fb->tree_conflict_local_node_kind == svn_node_none)
1862 fb->shadowed = TRUE;
1864 /* If this is not the merge target and the parent is too shallow to
1865 contain this directory, and the directory is not present
1866 via exclusion or depth filtering, skip it instead of recording
1869 Non-inheritable mergeinfo will be recorded, allowing
1870 future merges into non-shallow working copies to merge
1871 changes we missed this time around. */
1872 if (pdb && (excluded
1873 || (parent_depth != svn_depth_unknown &&
1874 parent_depth < svn_depth_files)))
1876 fb->shadowed = TRUE;
1878 fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1879 fb->skip_reason = svn_wc_notify_state_missing;
1880 return SVN_NO_ERROR;
1884 fb->tree_conflict_reason = svn_wc_conflict_reason_deleted;
1886 fb->tree_conflict_reason = svn_wc_conflict_reason_missing;
1888 /* ### Similar to directory */
1890 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1891 return SVN_NO_ERROR;
1894 else if (fb->tree_conflict_local_node_kind != svn_node_file)
1896 svn_boolean_t added;
1897 fb->shadowed = TRUE;
1899 SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
1900 local_abspath, scratch_pool));
1902 fb->tree_conflict_reason = added ? svn_wc_conflict_reason_added
1903 : svn_wc_conflict_reason_obstructed;
1905 /* ### Similar to directory */
1907 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1908 return SVN_NO_ERROR;
1914 /* We want to delete the directory */
1915 fb->tree_conflict_action = svn_wc_conflict_action_delete;
1916 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1920 return SVN_NO_ERROR; /* Already set a tree conflict */
1923 /* Comparison mode to verify for delete tree conflicts? */
1924 if (pdb && pdb->delete_state
1925 && pdb->delete_state->found_edit)
1927 /* Earlier nodes found a conflict. Done. */
1934 const svn_wc_conflict_description2_t *old_tc = NULL;
1936 /* The node doesn't exist pre-merge: We have an addition */
1938 fb->tree_conflict_action = svn_wc_conflict_action_add;
1940 if (pdb && pdb->pending_deletes
1941 && svn_hash_gets(pdb->pending_deletes, local_abspath))
1943 fb->add_is_replace = TRUE;
1944 fb->tree_conflict_action = svn_wc_conflict_action_replace;
1946 svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
1950 && pdb->new_tree_conflicts
1951 && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
1953 fb->tree_conflict_action = svn_wc_conflict_action_replace;
1954 fb->tree_conflict_reason = old_tc->reason;
1956 /* Update the tree conflict to store that this is a replace */
1957 SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
1959 old_tc->src_left_version->node_kind,
1961 fb->tree_conflict_action,
1962 fb->tree_conflict_reason,
1966 if (old_tc->reason == svn_wc_conflict_reason_deleted
1967 || old_tc->reason == svn_wc_conflict_reason_moved_away)
1969 /* Issue #3806: Incoming replacements on local deletes produce
1970 inconsistent result.
1972 In this specific case we can continue applying the add part
1973 of the replacement. */
1979 return SVN_NO_ERROR;
1982 else if (! (merge_b->dry_run
1983 && ((pdb && pdb->added) || fb->add_is_replace)))
1985 svn_wc_notify_state_t obstr_state;
1986 svn_boolean_t is_deleted;
1988 SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
1989 &fb->tree_conflict_local_node_kind,
1990 NULL, merge_b, local_abspath,
1993 if (obstr_state != svn_wc_notify_state_inapplicable)
1995 /* Skip the obstruction */
1996 fb->shadowed = TRUE;
1997 fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1998 fb->skip_reason = obstr_state;
2000 else if (fb->tree_conflict_local_node_kind != svn_node_none
2003 /* Set a tree conflict */
2004 svn_boolean_t added;
2006 fb->shadowed = TRUE;
2007 SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
2008 local_abspath, scratch_pool));
2010 fb->tree_conflict_reason = added ? svn_wc_conflict_reason_added
2011 : svn_wc_conflict_reason_obstructed;
2015 /* Handle pending conflicts */
2016 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2019 return SVN_NO_ERROR;
2022 /* An svn_diff_tree_processor_t function.
2024 * Called after merge_file_opened() when a node receives only text and/or
2025 * property changes between LEFT_SOURCE and RIGHT_SOURCE.
2027 * left_file and right_file can be NULL when the file is not modified.
2028 * left_props and right_props are always available.
2030 static svn_error_t *
2031 merge_file_changed(const char *relpath,
2032 const svn_diff_source_t *left_source,
2033 const svn_diff_source_t *right_source,
2034 const char *left_file,
2035 const char *right_file,
2036 /*const*/ apr_hash_t *left_props,
2037 /*const*/ apr_hash_t *right_props,
2038 svn_boolean_t file_modified,
2039 const apr_array_header_t *prop_changes,
2041 const struct svn_diff_tree_processor_t *processor,
2042 apr_pool_t *scratch_pool)
2044 merge_cmd_baton_t *merge_b = processor->baton;
2045 struct merge_file_baton_t *fb = file_baton;
2046 svn_client_ctx_t *ctx = merge_b->ctx;
2047 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2048 relpath, scratch_pool);
2049 const svn_wc_conflict_version_t *left;
2050 const svn_wc_conflict_version_t *right;
2051 svn_wc_notify_state_t text_state;
2052 svn_wc_notify_state_t property_state;
2054 SVN_ERR_ASSERT(local_abspath && svn_dirent_is_absolute(local_abspath));
2055 SVN_ERR_ASSERT(!left_file || svn_dirent_is_absolute(left_file));
2056 SVN_ERR_ASSERT(!right_file || svn_dirent_is_absolute(right_file));
2058 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2062 if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
2064 /* We haven't notified for this node yet: report a skip */
2065 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
2066 svn_wc_notify_update_shadowed_update,
2067 fb->skip_reason, fb->parent_baton,
2071 return SVN_NO_ERROR;
2074 /* This callback is essentially no more than a wrapper around
2075 svn_wc_merge5(). Thank goodness that all the
2076 diff-editor-mechanisms are doing the hard work of getting the
2079 property_state = svn_wc_notify_state_unchanged;
2080 text_state = svn_wc_notify_state_unchanged;
2082 SVN_ERR(prepare_merge_props_changed(&prop_changes, local_abspath,
2083 prop_changes, merge_b,
2084 scratch_pool, scratch_pool));
2086 SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
2087 svn_node_file, svn_node_file,
2088 &merge_b->merge_source, merge_b->target,
2089 scratch_pool, scratch_pool));
2091 /* Do property merge now, if we are not going to perform a text merge */
2092 if ((merge_b->record_only || !left_file) && prop_changes->nelts)
2094 SVN_ERR(svn_wc_merge_props3(&property_state, ctx->wc_ctx, local_abspath,
2096 left_props, prop_changes,
2099 ctx->cancel_func, ctx->cancel_baton,
2101 if (property_state == svn_wc_notify_state_conflicted)
2103 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
2108 /* Easy out: We are only applying mergeinfo differences. */
2109 if (merge_b->record_only)
2115 svn_boolean_t has_local_mods;
2116 enum svn_wc_merge_outcome_t content_outcome;
2117 const char *target_label;
2118 const char *left_label;
2119 const char *right_label;
2120 const char *path_ext = "";
2122 if (merge_b->ext_patterns && merge_b->ext_patterns->nelts)
2124 svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
2126 && svn_cstring_match_glob_list(path_ext,
2127 merge_b->ext_patterns)))
2133 /* xgettext: the '.working', '.merge-left.r%ld' and
2134 '.merge-right.r%ld' strings are used to tag onto a file
2135 name in case of a merge conflict */
2137 target_label = apr_psprintf(scratch_pool, _(".working%s%s"),
2138 *path_ext ? "." : "", path_ext);
2139 left_label = apr_psprintf(scratch_pool,
2140 _(".merge-left.r%ld%s%s"),
2141 left_source->revision,
2142 *path_ext ? "." : "", path_ext);
2143 right_label = apr_psprintf(scratch_pool,
2144 _(".merge-right.r%ld%s%s"),
2145 right_source->revision,
2146 *path_ext ? "." : "", path_ext);
2148 SVN_ERR(svn_wc_text_modified_p2(&has_local_mods, ctx->wc_ctx,
2149 local_abspath, FALSE, scratch_pool));
2151 /* Do property merge and text merge in one step so that keyword expansion
2152 takes into account the new property values. */
2153 SVN_ERR(svn_wc_merge5(&content_outcome, &property_state, ctx->wc_ctx,
2154 left_file, right_file, local_abspath,
2155 left_label, right_label, target_label,
2157 merge_b->dry_run, merge_b->diff3_cmd,
2158 merge_b->merge_options,
2159 left_props, prop_changes,
2165 if (content_outcome == svn_wc_merge_conflict
2166 || property_state == svn_wc_notify_state_conflicted)
2168 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
2172 if (content_outcome == svn_wc_merge_conflict)
2173 text_state = svn_wc_notify_state_conflicted;
2174 else if (has_local_mods
2175 && content_outcome != svn_wc_merge_unchanged)
2176 text_state = svn_wc_notify_state_merged;
2177 else if (content_outcome == svn_wc_merge_merged)
2178 text_state = svn_wc_notify_state_changed;
2179 else if (content_outcome == svn_wc_merge_no_merge)
2180 text_state = svn_wc_notify_state_missing;
2181 else /* merge_outcome == svn_wc_merge_unchanged */
2182 text_state = svn_wc_notify_state_unchanged;
2185 if (text_state == svn_wc_notify_state_conflicted
2186 || text_state == svn_wc_notify_state_merged
2187 || text_state == svn_wc_notify_state_changed
2188 || property_state == svn_wc_notify_state_conflicted
2189 || property_state == svn_wc_notify_state_merged
2190 || property_state == svn_wc_notify_state_changed)
2192 SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
2193 text_state, property_state,
2197 return SVN_NO_ERROR;
2200 /* An svn_diff_tree_processor_t function.
2202 * Called after merge_file_opened() when a node doesn't exist in LEFT_SOURCE,
2203 * but does in RIGHT_SOURCE.
2205 * When a node is replaced instead of just added a separate opened+deleted will
2206 * be invoked before the current open+added.
2208 static svn_error_t *
2209 merge_file_added(const char *relpath,
2210 const svn_diff_source_t *copyfrom_source,
2211 const svn_diff_source_t *right_source,
2212 const char *copyfrom_file,
2213 const char *right_file,
2214 /*const*/ apr_hash_t *copyfrom_props,
2215 /*const*/ apr_hash_t *right_props,
2217 const struct svn_diff_tree_processor_t *processor,
2218 apr_pool_t *scratch_pool)
2220 merge_cmd_baton_t *merge_b = processor->baton;
2221 struct merge_file_baton_t *fb = file_baton;
2222 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2223 relpath, scratch_pool);
2224 apr_hash_t *pristine_props;
2225 apr_hash_t *new_props;
2227 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2229 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2233 if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
2235 /* We haven't notified for this node yet: report a skip */
2236 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
2237 svn_wc_notify_update_shadowed_add,
2238 fb->skip_reason, fb->parent_baton,
2242 return SVN_NO_ERROR;
2245 /* Easy out: We are only applying mergeinfo differences. */
2246 if (merge_b->record_only)
2248 return SVN_NO_ERROR;
2251 if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
2252 && ( !fb->parent_baton || !fb->parent_baton->added))
2254 /* Store the roots of added subtrees */
2255 store_path(merge_b->added_abspaths, local_abspath);
2258 if (!merge_b->dry_run)
2260 const char *copyfrom_url;
2261 svn_revnum_t copyfrom_rev;
2262 svn_stream_t *new_contents, *pristine_contents;
2264 /* If this is a merge from the same repository as our
2265 working copy, we handle adds as add-with-history.
2266 Otherwise, we'll use a pure add. */
2267 if (merge_b->same_repos)
2270 svn_dirent_skip_ancestor(merge_b->target->abspath,
2272 SVN_ERR_ASSERT(child != NULL);
2273 copyfrom_url = svn_path_url_add_component2(
2274 merge_b->merge_source.loc2->url,
2275 child, scratch_pool);
2276 copyfrom_rev = right_source->revision;
2277 SVN_ERR(check_repos_match(merge_b->target, local_abspath,
2278 copyfrom_url, scratch_pool));
2279 SVN_ERR(svn_stream_open_readonly(&pristine_contents,
2283 new_contents = NULL; /* inherit from new_base_contents */
2285 pristine_props = right_props; /* Includes last_* information */
2286 new_props = NULL; /* No local changes */
2288 if (svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
2290 alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
2291 local_abspath, merge_b->pool);
2296 apr_array_header_t *regular_props;
2298 copyfrom_url = NULL;
2299 copyfrom_rev = SVN_INVALID_REVNUM;
2301 pristine_contents = svn_stream_empty(scratch_pool);
2302 SVN_ERR(svn_stream_open_readonly(&new_contents, right_file,
2303 scratch_pool, scratch_pool));
2305 pristine_props = apr_hash_make(scratch_pool); /* Local addition */
2307 /* We don't want any foreign properties */
2308 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props,
2310 NULL, NULL, ®ular_props,
2313 new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
2315 /* Issue #3383: We don't want mergeinfo from a foreign repository. */
2316 svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
2319 /* Do everything like if we had called 'svn cp PATH1 PATH2'. */
2320 SVN_ERR(svn_wc_add_repos_file4(merge_b->ctx->wc_ctx,
2324 pristine_props, new_props,
2325 copyfrom_url, copyfrom_rev,
2326 merge_b->ctx->cancel_func,
2327 merge_b->ctx->cancel_baton,
2330 /* Caller must call svn_sleep_for_timestamps() */
2331 *merge_b->use_sleep = TRUE;
2334 SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_file,
2335 fb->add_is_replace, scratch_pool));
2337 return SVN_NO_ERROR;
2340 /* Compare the two sets of properties PROPS1 and PROPS2, ignoring the
2341 * "svn:mergeinfo" property, and noticing only "normal" props. Set *SAME to
2342 * true if the rest of the properties are identical or false if they differ.
2344 static svn_error_t *
2345 properties_same_p(svn_boolean_t *same,
2348 apr_pool_t *scratch_pool)
2350 apr_array_header_t *prop_changes;
2353 /* Examine the properties that differ */
2354 SVN_ERR(svn_prop_diffs(&prop_changes, props1, props2, scratch_pool));
2356 for (i = 0; i < prop_changes->nelts; i++)
2358 const char *pname = APR_ARRAY_IDX(prop_changes, i, svn_prop_t).name;
2360 /* Count the properties we're interested in; ignore the rest */
2361 if (svn_wc_is_normal_prop(pname)
2362 && strcmp(pname, SVN_PROP_MERGEINFO) != 0)
2365 *same = (diffs == 0);
2366 return SVN_NO_ERROR;
2369 /* Compare the file OLDER_ABSPATH (together with its normal properties in
2370 * ORIGINAL_PROPS which may also contain WC props and entry props) with the
2371 * versioned file MINE_ABSPATH (together with its versioned properties).
2372 * Set *SAME to true if they are the same or false if they differ, ignoring
2373 * the "svn:mergeinfo" property, and ignoring differences in keyword
2374 * expansion and end-of-line style. */
2375 static svn_error_t *
2376 files_same_p(svn_boolean_t *same,
2377 const char *older_abspath,
2378 apr_hash_t *original_props,
2379 const char *mine_abspath,
2380 svn_wc_context_t *wc_ctx,
2381 apr_pool_t *scratch_pool)
2383 apr_hash_t *working_props;
2385 SVN_ERR(svn_wc_prop_list2(&working_props, wc_ctx, mine_abspath,
2386 scratch_pool, scratch_pool));
2388 /* Compare the properties */
2389 SVN_ERR(properties_same_p(same, original_props, working_props,
2393 svn_stream_t *mine_stream;
2394 svn_stream_t *older_stream;
2395 svn_string_t *special = svn_hash_gets(working_props, SVN_PROP_SPECIAL);
2396 svn_string_t *eol_style = svn_hash_gets(working_props, SVN_PROP_EOL_STYLE);
2397 svn_string_t *keywords = svn_hash_gets(working_props, SVN_PROP_KEYWORDS);
2399 /* Compare the file content, translating 'mine' to 'normal' form. */
2400 if (special != NULL)
2401 SVN_ERR(svn_subst_read_specialfile(&mine_stream, mine_abspath,
2402 scratch_pool, scratch_pool));
2404 SVN_ERR(svn_stream_open_readonly(&mine_stream, mine_abspath,
2405 scratch_pool, scratch_pool));
2407 if (!special && (eol_style || keywords))
2409 apr_hash_t *kw = NULL;
2410 const char *eol = NULL;
2411 svn_subst_eol_style_t style;
2413 /* We used to use svn_client__get_normalized_stream() here, but
2414 that doesn't work in 100% of the cases because it doesn't
2415 convert EOLs to the repository form; just to '\n'.
2420 svn_subst_eol_style_from_value(&style, &eol, eol_style->data);
2422 if (style == svn_subst_eol_style_native)
2423 eol = SVN_SUBST_NATIVE_EOL_STR;
2424 else if (style != svn_subst_eol_style_fixed
2425 && style != svn_subst_eol_style_none)
2426 return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL);
2430 SVN_ERR(svn_subst_build_keywords3(&kw, keywords->data, "", "",
2431 "", 0, "", scratch_pool));
2433 mine_stream = svn_subst_stream_translated(
2434 mine_stream, eol, FALSE, kw, FALSE, scratch_pool);
2437 SVN_ERR(svn_stream_open_readonly(&older_stream, older_abspath,
2438 scratch_pool, scratch_pool));
2440 SVN_ERR(svn_stream_contents_same2(same, mine_stream, older_stream,
2445 return SVN_NO_ERROR;
2448 /* An svn_diff_tree_processor_t function.
2450 * Called after merge_file_opened() when a node does exist in LEFT_SOURCE, but
2451 * no longer exists (or is replaced) in RIGHT_SOURCE.
2453 * When a node is replaced instead of just added a separate opened+added will
2454 * be invoked after the current open+deleted.
2456 static svn_error_t *
2457 merge_file_deleted(const char *relpath,
2458 const svn_diff_source_t *left_source,
2459 const char *left_file,
2460 /*const*/ apr_hash_t *left_props,
2462 const struct svn_diff_tree_processor_t *processor,
2463 apr_pool_t *scratch_pool)
2465 merge_cmd_baton_t *merge_b = processor->baton;
2466 struct merge_file_baton_t *fb = file_baton;
2467 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2468 relpath, scratch_pool);
2471 SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2475 if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
2477 /* We haven't notified for this node yet: report a skip */
2478 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
2479 svn_wc_notify_update_shadowed_delete,
2480 fb->skip_reason, fb->parent_baton,
2484 return SVN_NO_ERROR;
2487 /* Easy out: We are only applying mergeinfo differences. */
2488 if (merge_b->record_only)
2490 return SVN_NO_ERROR;
2493 /* If the files are identical, attempt deletion */
2494 if (merge_b->force_delete)
2497 SVN_ERR(files_same_p(&same, left_file, left_props,
2498 local_abspath, merge_b->ctx->wc_ctx,
2501 if (fb->parent_baton
2502 && fb->parent_baton->delete_state)
2506 /* Note that we checked this file */
2507 store_path(fb->parent_baton->delete_state->compared_abspaths,
2512 /* We found some modification. Parent should raise a tree conflict */
2513 fb->parent_baton->delete_state->found_edit = TRUE;
2516 return SVN_NO_ERROR;
2520 if (!merge_b->dry_run)
2521 SVN_ERR(svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
2522 FALSE /* keep_local */, FALSE /* unversioned */,
2523 merge_b->ctx->cancel_func,
2524 merge_b->ctx->cancel_baton,
2525 NULL, NULL /* no notify */,
2528 /* Record that we might have deleted mergeinfo */
2529 alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
2530 local_abspath, merge_b->pool);
2532 /* And notify the deletion */
2533 SVN_ERR(record_update_delete(merge_b, fb->parent_baton, local_abspath,
2534 svn_node_file, scratch_pool));
2538 /* The files differ, so raise a conflict instead of deleting */
2540 /* This is use case 5 described in the paper attached to issue
2541 * #2282. See also notes/tree-conflicts/detection.txt
2543 SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
2547 svn_wc_conflict_action_delete,
2548 svn_wc_conflict_reason_edited,
2553 return SVN_NO_ERROR;
2556 /* An svn_diff_tree_processor_t function.
2558 Called before either merge_dir_changed(), merge_dir_added(),
2559 merge_dir_deleted() or merge_dir_closed(), unless it sets *SKIP to TRUE.
2561 After this call and before the close call, all descendants will receive
2562 their changes, unless *SKIP_CHILDREN is set to TRUE.
2564 When *SKIP is TRUE, the diff driver avoids work on getting the details
2565 for the closing callbacks.
2567 The SKIP and SKIP_DESCENDANTS work independently.
2569 static svn_error_t *
2570 merge_dir_opened(void **new_dir_baton,
2571 svn_boolean_t *skip,
2572 svn_boolean_t *skip_children,
2573 const char *relpath,
2574 const svn_diff_source_t *left_source,
2575 const svn_diff_source_t *right_source,
2576 const svn_diff_source_t *copyfrom_source,
2577 void *parent_dir_baton,
2578 const struct svn_diff_tree_processor_t *processor,
2579 apr_pool_t *result_pool,
2580 apr_pool_t *scratch_pool)
2582 merge_cmd_baton_t *merge_b = processor->baton;
2583 struct merge_dir_baton_t *db;
2584 struct merge_dir_baton_t *pdb = parent_dir_baton;
2586 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2587 relpath, scratch_pool);
2589 db = apr_pcalloc(result_pool, sizeof(*db));
2590 db->pool = result_pool;
2591 db->tree_conflict_reason = CONFLICT_REASON_NONE;
2592 db->tree_conflict_action = svn_wc_conflict_action_edit;
2593 db->skip_reason = svn_wc_notify_state_unknown;
2595 *new_dir_baton = db;
2598 db->tree_conflict_merge_left_node_kind = svn_node_dir;
2600 db->tree_conflict_merge_left_node_kind = svn_node_none;
2603 db->tree_conflict_merge_right_node_kind = svn_node_dir;
2605 db->tree_conflict_merge_right_node_kind = svn_node_none;
2609 db->parent_baton = pdb;
2610 db->shadowed = pdb->shadowed;
2611 db->skip_reason = pdb->skip_reason;
2616 /* An ancestor is tree conflicted. Nothing to do here. */
2620 else if (left_source != NULL)
2622 /* Node is expected to be a directory. */
2623 svn_boolean_t is_deleted;
2624 svn_boolean_t excluded;
2625 svn_depth_t parent_depth;
2628 db->tree_conflict_action = svn_wc_conflict_action_delete;
2630 /* Check for an obstructed or missing node on disk. */
2632 svn_wc_notify_state_t obstr_state;
2633 SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
2634 &db->tree_conflict_local_node_kind,
2635 &parent_depth, merge_b,
2636 local_abspath, scratch_pool));
2638 if (obstr_state != svn_wc_notify_state_inapplicable)
2640 db->shadowed = TRUE;
2642 if (obstr_state == svn_wc_notify_state_obstructed)
2644 svn_boolean_t is_wcroot;
2646 SVN_ERR(svn_wc_check_root(&is_wcroot, NULL, NULL,
2647 merge_b->ctx->wc_ctx,
2648 local_abspath, scratch_pool));
2652 db->tree_conflict_reason = CONFLICT_REASON_SKIP_WC;
2653 return SVN_NO_ERROR;
2657 db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2658 db->skip_reason = obstr_state;
2662 *skip = *skip_children = TRUE;
2663 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath,
2667 return SVN_NO_ERROR;
2671 db->tree_conflict_local_node_kind = svn_node_none;
2674 if (db->tree_conflict_local_node_kind == svn_node_none)
2676 db->shadowed = TRUE;
2678 /* If this is not the merge target and the parent is too shallow to
2679 contain this directory, and the directory is not presen
2680 via exclusion or depth filtering, skip it instead of recording
2683 Non-inheritable mergeinfo will be recorded, allowing
2684 future merges into non-shallow working copies to merge
2685 changes we missed this time around. */
2686 if (pdb && (excluded
2687 || (parent_depth != svn_depth_unknown &&
2688 parent_depth < svn_depth_immediates)))
2690 db->shadowed = TRUE;
2692 db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2693 db->skip_reason = svn_wc_notify_state_missing;
2695 return SVN_NO_ERROR;
2699 db->tree_conflict_reason = svn_wc_conflict_reason_deleted;
2701 db->tree_conflict_reason = svn_wc_conflict_reason_missing;
2703 /* ### To avoid breaking tests */
2705 *skip_children = TRUE;
2706 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2707 return SVN_NO_ERROR;
2708 /* ### /avoid breaking tests */
2710 else if (db->tree_conflict_local_node_kind != svn_node_dir)
2712 svn_boolean_t added;
2714 db->shadowed = TRUE;
2715 SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
2716 local_abspath, scratch_pool));
2718 db->tree_conflict_reason = added ? svn_wc_conflict_reason_added
2719 : svn_wc_conflict_reason_obstructed;
2721 /* ### To avoid breaking tests */
2723 *skip_children = TRUE;
2724 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2725 return SVN_NO_ERROR;
2726 /* ### /avoid breaking tests */
2731 /* We want to delete the directory */
2732 /* Mark PB edited now? */
2733 db->tree_conflict_action = svn_wc_conflict_action_delete;
2734 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2738 *skip_children = TRUE;
2739 return SVN_NO_ERROR; /* Already set a tree conflict */
2742 db->delete_state = (pdb != NULL) ? pdb->delete_state : NULL;
2744 if (db->delete_state && db->delete_state->found_edit)
2746 /* A sibling found a conflict. Done. */
2748 *skip_children = TRUE;
2750 else if (merge_b->force_delete)
2752 /* No comparison necessary */
2753 *skip_children = TRUE;
2755 else if (! db->delete_state)
2757 /* Start descendant comparison */
2758 db->delete_state = apr_pcalloc(db->pool,
2759 sizeof(*db->delete_state));
2761 db->delete_state->del_root = db;
2762 db->delete_state->compared_abspaths = apr_hash_make(db->pool);
2768 const svn_wc_conflict_description2_t *old_tc = NULL;
2770 /* The node doesn't exist pre-merge: We have an addition */
2772 db->tree_conflict_action = svn_wc_conflict_action_add;
2774 if (pdb && pdb->pending_deletes
2775 && svn_hash_gets(pdb->pending_deletes, local_abspath))
2777 db->add_is_replace = TRUE;
2778 db->tree_conflict_action = svn_wc_conflict_action_replace;
2780 svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
2784 && pdb->new_tree_conflicts
2785 && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
2787 db->tree_conflict_action = svn_wc_conflict_action_replace;
2788 db->tree_conflict_reason = old_tc->reason;
2790 if (old_tc->reason == svn_wc_conflict_reason_deleted
2791 || old_tc->reason == svn_wc_conflict_reason_moved_away)
2793 /* Issue #3806: Incoming replacements on local deletes produce
2794 inconsistent result.
2796 In this specific case we can continue applying the add part
2797 of the replacement. */
2802 *skip_children = TRUE;
2804 /* Update the tree conflict to store that this is a replace */
2805 SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
2807 old_tc->src_left_version->node_kind,
2809 db->tree_conflict_action,
2810 db->tree_conflict_reason,
2814 return SVN_NO_ERROR;
2818 if (! (merge_b->dry_run
2819 && ((pdb && pdb->added) || db->add_is_replace)))
2821 svn_wc_notify_state_t obstr_state;
2822 svn_boolean_t is_deleted;
2824 SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
2825 &db->tree_conflict_local_node_kind,
2826 NULL, merge_b, local_abspath,
2829 /* In this case of adding a directory, we have an exception to the
2830 * usual "skip if it's inconsistent" rule. If the directory exists
2831 * on disk unexpectedly, we simply make it versioned, because we can
2832 * do so without risk of destroying data. Only skip if it is
2833 * versioned but unexpectedly missing from disk, or is unversioned
2834 * but obstructed by a node of the wrong kind. */
2835 if (obstr_state == svn_wc_notify_state_obstructed
2837 db->tree_conflict_local_node_kind == svn_node_none))
2839 svn_node_kind_t disk_kind;
2841 SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
2844 if (disk_kind == svn_node_dir)
2846 obstr_state = svn_wc_notify_state_inapplicable;
2847 db->add_existing = TRUE; /* Take over existing directory */
2851 if (obstr_state != svn_wc_notify_state_inapplicable)
2853 /* Skip the obstruction */
2854 db->shadowed = TRUE;
2855 db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2856 db->skip_reason = obstr_state;
2858 else if (db->tree_conflict_local_node_kind != svn_node_none
2861 /* Set a tree conflict */
2862 svn_boolean_t added;
2863 db->shadowed = TRUE;
2865 SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
2866 local_abspath, scratch_pool));
2868 db->tree_conflict_reason = added ? svn_wc_conflict_reason_added
2869 : svn_wc_conflict_reason_obstructed;
2873 /* Handle pending conflicts */
2874 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2878 /* Notified and done. Skip children? */
2880 else if (merge_b->record_only)
2882 /* Ok, we are done for this node and its descendants */
2884 *skip_children = TRUE;
2886 else if (! merge_b->dry_run)
2888 /* Create the directory on disk, to allow descendants to be added */
2889 if (! db->add_existing)
2890 SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT,
2895 /* svn_wc_add4 and svn_wc_add_from_disk3 can't add a node
2896 over an existing tree conflict */
2898 /* ### These functions should take some tree conflict argument
2899 and allow overwriting the tc when one is passed */
2901 SVN_ERR(svn_wc__del_tree_conflict(merge_b->ctx->wc_ctx,
2906 if (merge_b->same_repos)
2908 const char *original_url;
2910 original_url = svn_path_url_add_component2(
2911 merge_b->merge_source.loc2->url,
2912 relpath, scratch_pool);
2914 /* Limitation (aka HACK):
2915 We create a newly added directory with an original URL and
2916 revision as that in the repository, but without its properties
2919 When the merge is cancelled before the final dir_added(), the
2920 copy won't really represent the in-repository state of the node.
2922 SVN_ERR(svn_wc_add4(merge_b->ctx->wc_ctx, local_abspath,
2925 right_source->revision,
2926 merge_b->ctx->cancel_func,
2927 merge_b->ctx->cancel_baton,
2928 NULL, NULL /* no notify! */,
2933 SVN_ERR(svn_wc_add_from_disk3(merge_b->ctx->wc_ctx, local_abspath,
2934 apr_hash_make(scratch_pool),
2935 FALSE /* skip checks */,
2936 NULL, NULL /* no notify! */,
2942 /* ### Should be atomic with svn_wc_add(4|_from_disk2)() */
2943 SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
2947 db->tree_conflict_action,
2948 db->tree_conflict_reason,
2954 if (! db->shadowed && !merge_b->record_only)
2955 SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_dir,
2956 db->add_is_replace, scratch_pool));
2958 return SVN_NO_ERROR;
2961 /* An svn_diff_tree_processor_t function.
2963 * Called after merge_dir_opened() when a node exists in both the left and
2964 * right source, but has its properties changed inbetween.
2966 * After the merge_dir_opened() but before the call to this merge_dir_changed()
2967 * function all descendants will have been updated.
2969 static svn_error_t *
2970 merge_dir_changed(const char *relpath,
2971 const svn_diff_source_t *left_source,
2972 const svn_diff_source_t *right_source,
2973 /*const*/ apr_hash_t *left_props,
2974 /*const*/ apr_hash_t *right_props,
2975 const apr_array_header_t *prop_changes,
2977 const struct svn_diff_tree_processor_t *processor,
2978 apr_pool_t *scratch_pool)
2980 merge_cmd_baton_t *merge_b = processor->baton;
2981 struct merge_dir_baton_t *db = dir_baton;
2982 const apr_array_header_t *props;
2983 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2984 relpath, scratch_pool);
2986 SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
2988 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2992 if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
2994 /* We haven't notified for this node yet: report a skip */
2995 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
2996 svn_wc_notify_update_shadowed_update,
2997 db->skip_reason, db->parent_baton,
3001 return SVN_NO_ERROR;
3004 SVN_ERR(prepare_merge_props_changed(&props, local_abspath, prop_changes,
3005 merge_b, scratch_pool, scratch_pool));
3009 const svn_wc_conflict_version_t *left;
3010 const svn_wc_conflict_version_t *right;
3011 svn_client_ctx_t *ctx = merge_b->ctx;
3012 svn_wc_notify_state_t prop_state;
3014 SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
3015 svn_node_dir, svn_node_dir,
3016 &merge_b->merge_source,
3018 scratch_pool, scratch_pool));
3020 SVN_ERR(svn_wc_merge_props3(&prop_state, ctx->wc_ctx, local_abspath,
3025 ctx->cancel_func, ctx->cancel_baton,
3028 if (prop_state == svn_wc_notify_state_conflicted)
3030 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
3034 if (prop_state == svn_wc_notify_state_conflicted
3035 || prop_state == svn_wc_notify_state_merged
3036 || prop_state == svn_wc_notify_state_changed)
3038 SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
3039 svn_wc_notify_state_inapplicable,
3040 prop_state, scratch_pool));
3044 return SVN_NO_ERROR;
3048 /* An svn_diff_tree_processor_t function.
3050 * Called after merge_dir_opened() when a node doesn't exist in LEFT_SOURCE,
3051 * but does in RIGHT_SOURCE. After the merge_dir_opened() but before the call
3052 * to this merge_dir_added() function all descendants will have been added.
3054 * When a node is replaced instead of just added a separate opened+deleted will
3055 * be invoked before the current open+added.
3057 static svn_error_t *
3058 merge_dir_added(const char *relpath,
3059 const svn_diff_source_t *copyfrom_source,
3060 const svn_diff_source_t *right_source,
3061 /*const*/ apr_hash_t *copyfrom_props,
3062 /*const*/ apr_hash_t *right_props,
3064 const struct svn_diff_tree_processor_t *processor,
3065 apr_pool_t *scratch_pool)
3067 merge_cmd_baton_t *merge_b = processor->baton;
3068 struct merge_dir_baton_t *db = dir_baton;
3069 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
3070 relpath, scratch_pool);
3072 /* For consistency; usually a no-op from _dir_added() */
3073 SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
3074 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
3078 if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
3080 /* We haven't notified for this node yet: report a skip */
3081 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
3082 svn_wc_notify_update_shadowed_add,
3083 db->skip_reason, db->parent_baton,
3087 return SVN_NO_ERROR;
3091 db->edited /* Marked edited from merge_open_dir() */
3092 && ! merge_b->record_only /* Skip details from merge_open_dir() */
3095 if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
3096 && ( !db->parent_baton || !db->parent_baton->added))
3098 /* Store the roots of added subtrees */
3099 store_path(merge_b->added_abspaths, local_abspath);
3102 if (merge_b->same_repos)
3104 /* When the directory was added in merge_dir_added() we didn't update its
3105 pristine properties. Instead we receive the property changes later and
3106 apply them in this function.
3108 If we would apply them as changes (such as before fixing issue #3405),
3109 we would see the unmodified properties as local changes, and the
3110 pristine properties would be out of sync with what the repository
3111 expects for this directory.
3113 Instead of doing that we now simply set the properties as the pristine
3114 properties via a private libsvn_wc api.
3117 const char *copyfrom_url;
3118 svn_revnum_t copyfrom_rev;
3119 const char *parent_abspath;
3122 /* Creating a hash containing regular and entry props */
3123 apr_hash_t *new_pristine_props = right_props;
3125 parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3126 child = svn_dirent_is_child(merge_b->target->abspath, local_abspath, NULL);
3127 SVN_ERR_ASSERT(child != NULL);
3129 copyfrom_url = svn_path_url_add_component2(merge_b->merge_source.loc2->url,
3130 child, scratch_pool);
3131 copyfrom_rev = right_source->revision;
3133 SVN_ERR(check_repos_match(merge_b->target, parent_abspath, copyfrom_url,
3136 if (!merge_b->dry_run)
3138 SVN_ERR(svn_wc__complete_directory_add(merge_b->ctx->wc_ctx,
3141 copyfrom_url, copyfrom_rev,
3145 if (svn_hash_gets(new_pristine_props, SVN_PROP_MERGEINFO))
3147 alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
3148 local_abspath, merge_b->pool);
3153 apr_array_header_t *regular_props;
3154 apr_hash_t *new_props;
3155 svn_wc_notify_state_t prop_state;
3157 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props,
3159 NULL, NULL, ®ular_props, scratch_pool));
3161 new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
3163 svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
3165 /* ### What is the easiest way to set new_props on LOCAL_ABSPATH?
3167 ### This doesn't need a merge as we just added the node
3168 ### (or installed a tree conflict and skipped this node)*/
3170 SVN_ERR(svn_wc_merge_props3(&prop_state, merge_b->ctx->wc_ctx,
3173 apr_hash_make(scratch_pool),
3174 svn_prop_hash_to_array(new_props,
3178 merge_b->ctx->cancel_func,
3179 merge_b->ctx->cancel_baton,
3181 if (prop_state == svn_wc_notify_state_conflicted)
3183 alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
3188 return SVN_NO_ERROR;
3191 /* Helper for merge_dir_deleted. Implement svn_wc_status_func4_t */
3192 static svn_error_t *
3193 verify_touched_by_del_check(void *baton,
3194 const char *local_abspath,
3195 const svn_wc_status3_t *status,
3196 apr_pool_t *scratch_pool)
3198 struct dir_delete_baton_t *delb = baton;
3200 if (svn_hash_gets(delb->compared_abspaths, local_abspath))
3201 return SVN_NO_ERROR;
3203 switch (status->node_status)
3205 case svn_wc_status_deleted:
3206 case svn_wc_status_ignored:
3207 case svn_wc_status_none:
3208 return SVN_NO_ERROR;
3211 delb->found_edit = TRUE;
3212 return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
3216 /* An svn_diff_tree_processor_t function.
3218 * Called after merge_dir_opened() when a node existed only in the left source.
3220 * After the merge_dir_opened() but before the call to this merge_dir_deleted()
3221 * function all descendants that existed in left_source will have been deleted.
3223 * If this node is replaced, an _opened() followed by a matching _add() will
3224 * be invoked after this function.
3226 static svn_error_t *
3227 merge_dir_deleted(const char *relpath,
3228 const svn_diff_source_t *left_source,
3229 /*const*/ apr_hash_t *left_props,
3231 const struct svn_diff_tree_processor_t *processor,
3232 apr_pool_t *scratch_pool)
3234 merge_cmd_baton_t *merge_b = processor->baton;
3235 struct merge_dir_baton_t *db = dir_baton;
3236 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
3237 relpath, scratch_pool);
3239 apr_hash_t *working_props;
3241 SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
3242 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
3246 if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
3248 /* We haven't notified for this node yet: report a skip */
3249 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
3250 svn_wc_notify_update_shadowed_delete,
3251 db->skip_reason, db->parent_baton,
3255 return SVN_NO_ERROR;
3258 /* Easy out: We are only applying mergeinfo differences. */
3259 if (merge_b->record_only)
3261 return SVN_NO_ERROR;
3264 SVN_ERR(svn_wc_prop_list2(&working_props,
3265 merge_b->ctx->wc_ctx, local_abspath,
3266 scratch_pool, scratch_pool));
3268 if (merge_b->force_delete)
3270 /* In this legacy mode we just assume that a directory delete
3271 matches any directory. db->delete_state is NULL */
3276 struct dir_delete_baton_t *delb;
3278 /* Compare the properties */
3279 SVN_ERR(properties_same_p(&same, left_props, working_props,
3281 delb = db->delete_state;
3282 assert(delb != NULL);
3286 delb->found_edit = TRUE;
3290 store_path(delb->compared_abspaths, local_abspath);
3293 if (delb->del_root != db)
3294 return SVN_NO_ERROR;
3296 if (delb->found_edit)
3300 apr_array_header_t *ignores;
3304 SVN_ERR(svn_wc_get_default_ignores(&ignores, merge_b->ctx->config,
3307 /* None of the descendants was modified, but maybe there are
3308 descendants we haven't walked?
3310 Note that we aren't interested in changes, as we already verified
3311 changes in the paths touched by the merge. And the existence of
3312 other paths is enough to mark the directory edited */
3313 err = svn_wc_walk_status(merge_b->ctx->wc_ctx, local_abspath,
3314 svn_depth_infinity, TRUE /* get-all */,
3315 FALSE /* no-ignore */,
3316 TRUE /* ignore-text-mods */, ignores,
3317 verify_touched_by_del_check, delb,
3318 merge_b->ctx->cancel_func,
3319 merge_b->ctx->cancel_baton,
3324 if (err->apr_err != SVN_ERR_CEASE_INVOCATION)
3325 return svn_error_trace(err);
3327 svn_error_clear(err);
3330 same = ! delb->found_edit;
3334 if (same && !merge_b->dry_run)
3338 err = svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
3339 FALSE /* keep_local */, FALSE /* unversioned */,
3340 merge_b->ctx->cancel_func,
3341 merge_b->ctx->cancel_baton,
3342 NULL, NULL /* no notify */,
3347 if (err->apr_err != SVN_ERR_WC_LEFT_LOCAL_MOD)
3348 return svn_error_trace(err);
3350 svn_error_clear(err);
3357 /* If the attempt to delete an existing directory failed,
3358 * the directory has local modifications (e.g. locally added
3359 * files, or property changes). Flag a tree conflict. */
3361 /* This handles use case 5 described in the paper attached to issue
3362 * #2282. See also notes/tree-conflicts/detection.txt
3364 SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
3368 svn_wc_conflict_action_delete,
3369 svn_wc_conflict_reason_edited,
3375 /* Record that we might have deleted mergeinfo */
3377 && svn_hash_gets(working_props, SVN_PROP_MERGEINFO))
3379 alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
3380 local_abspath, merge_b->pool);
3383 SVN_ERR(record_update_delete(merge_b, db->parent_baton, local_abspath,
3384 svn_node_dir, scratch_pool));
3387 return SVN_NO_ERROR;
3390 /* An svn_diff_tree_processor_t function.
3392 * Called after merge_dir_opened() when a node itself didn't change between
3393 * the left and right source.
3395 * After the merge_dir_opened() but before the call to this merge_dir_closed()
3396 * function all descendants will have been processed.
3398 static svn_error_t *
3399 merge_dir_closed(const char *relpath,
3400 const svn_diff_source_t *left_source,
3401 const svn_diff_source_t *right_source,
3403 const struct svn_diff_tree_processor_t *processor,
3404 apr_pool_t *scratch_pool)
3406 merge_cmd_baton_t *merge_b = processor->baton;
3407 struct merge_dir_baton_t *db = dir_baton;
3409 SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
3411 return SVN_NO_ERROR;
3414 /* An svn_diff_tree_processor_t function.
3416 Called when the diff driver wants to report an absent path.
3418 In case of merges this happens when the diff encounters a server-excluded
3421 We register a skipped path, which will make parent mergeinfo non-
3422 inheritable. This ensures that a future merge might see these skipped
3423 changes as eligable for merging.
3425 For legacy reasons we also notify the path as skipped.
3427 static svn_error_t *
3428 merge_node_absent(const char *relpath,
3430 const svn_diff_tree_processor_t *processor,
3431 apr_pool_t *scratch_pool)
3433 merge_cmd_baton_t *merge_b = processor->baton;
3434 struct merge_dir_baton_t *db = dir_baton;
3436 const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
3437 relpath, scratch_pool);
3439 SVN_ERR(record_skip(merge_b, local_abspath, svn_node_unknown,
3440 svn_wc_notify_skip, svn_wc_notify_state_missing,
3443 return SVN_NO_ERROR;
3446 /*-----------------------------------------------------------------------*/
3448 /*** Merge Notification ***/
3451 /* Finds a nearest ancestor in CHILDREN_WITH_MERGEINFO for LOCAL_ABSPATH. If
3452 PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
3453 where child->abspath == PATH is considered PATH's ancestor. If FALSE,
3454 then child->abspath must be a proper ancestor of PATH.
3456 CHILDREN_WITH_MERGEINFO is expected to be sorted in Depth first
3458 static svn_client__merge_path_t *
3459 find_nearest_ancestor(const apr_array_header_t *children_with_mergeinfo,
3460 svn_boolean_t path_is_own_ancestor,
3461 const char *local_abspath)
3465 SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
3467 for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
3469 svn_client__merge_path_t *child =
3470 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
3472 if (svn_dirent_is_ancestor(child->abspath, local_abspath)
3473 && (path_is_own_ancestor
3474 || strcmp(child->abspath, local_abspath) != 0))
3480 /* Find the highest level path in a merge target (possibly the merge target
3481 itself) to use in a merge notification header.
3483 Return the svn_client__merge_path_t * representing the most distant
3484 ancestor in CHILDREN_WITH_MERGEINFO of LOCAL_ABSPATH where said
3485 ancestor's first remaining ranges element (per the REMAINING_RANGES
3486 member of the ancestor) intersect with the first remaining ranges element
3487 for every intermediate ancestor svn_client__merge_path_t * of
3488 LOCAL_ABSPATH. If no such ancestor is found return NULL.
3490 If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
3491 represent a forward merge, then set *START to the oldest revision found
3492 in any of the intersecting ancestors and *END to the youngest revision
3493 found. If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
3494 represent a reverse merge, then set *START to the youngest revision
3495 found and *END to the oldest revision found. If no ancestors are found
3496 then set *START and *END to SVN_INVALID_REVNUM.
3498 If PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
3499 where child->abspath == PATH is considered PATH's ancestor. If FALSE,
3500 then child->abspath must be a proper ancestor of PATH.
3502 See the CHILDREN_WITH_MERGEINFO ARRAY global comment for more
3504 static svn_client__merge_path_t *
3505 find_nearest_ancestor_with_intersecting_ranges(
3506 svn_revnum_t *start,
3508 const apr_array_header_t *children_with_mergeinfo,
3509 svn_boolean_t path_is_own_ancestor,
3510 const char *local_abspath)
3513 svn_client__merge_path_t *nearest_ancestor = NULL;
3515 *start = SVN_INVALID_REVNUM;
3516 *end = SVN_INVALID_REVNUM;
3518 SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
3520 for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
3522 svn_client__merge_path_t *child =
3523 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
3525 if (svn_dirent_is_ancestor(child->abspath, local_abspath)
3526 && (path_is_own_ancestor
3527 || strcmp(child->abspath, local_abspath) != 0))
3529 if (nearest_ancestor == NULL)
3531 /* Found an ancestor. */
3532 nearest_ancestor = child;
3534 if (child->remaining_ranges)
3536 svn_merge_range_t *r1 = APR_ARRAY_IDX(
3537 child->remaining_ranges, 0, svn_merge_range_t *);
3543 /* If CHILD->REMAINING_RANGES is null then LOCAL_ABSPATH
3544 is inside an absent subtree in the merge target. */
3545 *start = SVN_INVALID_REVNUM;
3546 *end = SVN_INVALID_REVNUM;
3552 /* We'e found another ancestor for LOCAL_ABSPATH. Do its
3553 first remaining range intersect with the previously
3555 svn_merge_range_t *r1 =
3556 APR_ARRAY_IDX(nearest_ancestor->remaining_ranges, 0,
3557 svn_merge_range_t *);
3558 svn_merge_range_t *r2 =
3559 APR_ARRAY_IDX(child->remaining_ranges, 0,
3560 svn_merge_range_t *);
3564 svn_merge_range_t range1;
3565 svn_merge_range_t range2;
3566 svn_boolean_t reverse_merge = r1->start > r2->end;
3568 /* Flip endpoints if this is a reverse merge. */
3571 range1.start = r1->end;
3572 range1.end = r1->start;
3573 range2.start = r2->end;
3574 range2.end = r2->start;
3578 range1.start = r1->start;
3579 range1.end = r1->end;
3580 range2.start = r2->start;
3581 range2.end = r2->end;
3584 if (range1.start < range2.end && range2.start < range1.end)
3586 *start = reverse_merge ?
3587 MAX(r1->start, r2->start) : MIN(r1->start, r2->start);
3588 *end = reverse_merge ?
3589 MIN(r1->end, r2->end) : MAX(r1->end, r2->end);
3590 nearest_ancestor = child;
3596 return nearest_ancestor;
3599 /* Notify that we're starting to record mergeinfo for the merge of the
3600 * revision range RANGE into TARGET_ABSPATH. RANGE should be null if the
3601 * merge sources are not from the same URL.
3603 * This calls the client's notification receiver (as found in the client
3604 * context), with a WC abspath.
3607 notify_mergeinfo_recording(const char *target_abspath,
3608 const svn_merge_range_t *range,
3609 svn_client_ctx_t *ctx,
3612 if (ctx->notify_func2)
3614 svn_wc_notify_t *n = svn_wc_create_notify(
3615 target_abspath, svn_wc_notify_merge_record_info_begin, pool);
3617 n->merge_range = range ? svn_merge_range_dup(range, pool) : NULL;
3618 ctx->notify_func2(ctx->notify_baton2, n, pool);
3622 /* Notify that we're completing the merge into TARGET_ABSPATH.
3624 * This calls the client's notification receiver (as found in the client
3625 * context), with a WC abspath.
3628 notify_merge_completed(const char *target_abspath,
3629 svn_client_ctx_t *ctx,
3632 if (ctx->notify_func2)
3635 = svn_wc_create_notify(target_abspath, svn_wc_notify_merge_completed,
3637 ctx->notify_func2(ctx->notify_baton2, n, pool);
3641 /* Is the notification the result of a real operative merge? */
3642 #define IS_OPERATIVE_NOTIFICATION(notify) \
3643 (notify->content_state == svn_wc_notify_state_conflicted \
3644 || notify->content_state == svn_wc_notify_state_merged \
3645 || notify->content_state == svn_wc_notify_state_changed \
3646 || notify->prop_state == svn_wc_notify_state_conflicted \
3647 || notify->prop_state == svn_wc_notify_state_merged \
3648 || notify->prop_state == svn_wc_notify_state_changed \
3649 || notify->action == svn_wc_notify_update_add \
3650 || notify->action == svn_wc_notify_tree_conflict)
3653 /* Remove merge source gaps from range used for merge notifications.
3654 See http://subversion.tigris.org/issues/show_bug.cgi?id=4138
3656 If IMPLICIT_SRC_GAP is not NULL then it is a rangelist containing a
3657 single range (see the implicit_src_gap member of merge_cmd_baton_t).
3658 RANGE describes a (possibly reverse) merge.
3660 If IMPLICIT_SRC_GAP is not NULL and it's sole range intersects with
3661 the older revision in *RANGE, then remove IMPLICIT_SRC_GAP's range
3664 remove_source_gap(svn_merge_range_t *range,
3665 apr_array_header_t *implicit_src_gap)
3667 if (implicit_src_gap)
3669 svn_merge_range_t *gap_range =
3670 APR_ARRAY_IDX(implicit_src_gap, 0, svn_merge_range_t *);
3671 if (range->start < range->end)
3673 if (gap_range->start == range->start)
3674 range->start = gap_range->end;
3676 else /* Reverse merge */
3678 if (gap_range->start == range->end)
3679 range->end = gap_range->end;
3684 /* Notify that we're starting a merge
3686 * This calls the client's notification receiver (as found in the client
3687 * context), with a WC abspath.
3689 static svn_error_t *
3690 notify_merge_begin(merge_cmd_baton_t *merge_b,
3691 const char *local_abspath,
3692 svn_boolean_t delete_action,
3693 apr_pool_t *scratch_pool)
3695 svn_wc_notify_t *notify;
3696 svn_merge_range_t n_range =
3697 {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE};
3698 const char *notify_abspath;
3700 if (! merge_b->ctx->notify_func2)
3701 return SVN_NO_ERROR;
3703 /* If our merge sources are ancestors of one another... */
3704 if (merge_b->merge_source.ancestral)
3706 const svn_client__merge_path_t *child;
3707 /* Find NOTIFY->PATH's nearest ancestor in
3708 NOTIFY->CHILDREN_WITH_MERGEINFO. Normally we consider a child in
3709 NOTIFY->CHILDREN_WITH_MERGEINFO representing PATH to be an
3710 ancestor of PATH, but if this is a deletion of PATH then the
3711 notification must be for a proper ancestor of PATH. This ensures
3712 we don't get notifications like:
3714 --- Merging rX into 'PARENT/CHILD'
3719 --- Merging rX into 'PARENT'
3723 child = find_nearest_ancestor_with_intersecting_ranges(
3724 &(n_range.start), &(n_range.end),
3725 merge_b->notify_begin.nodes_with_mergeinfo,
3726 ! delete_action, local_abspath);
3728 if (!child && delete_action)
3730 /* Triggered by file replace in single-file-merge */
3731 child = find_nearest_ancestor(merge_b->notify_begin.nodes_with_mergeinfo,
3732 TRUE, local_abspath);
3735 assert(child != NULL); /* Should always find the merge anchor */
3738 return SVN_NO_ERROR;
3740 if (merge_b->notify_begin.last_abspath != NULL
3741 && strcmp(child->abspath, merge_b->notify_begin.last_abspath) == 0)
3743 /* Don't notify the same merge again */
3744 return SVN_NO_ERROR;
3747 merge_b->notify_begin.last_abspath = child->abspath;
3749 if (child->absent || child->remaining_ranges->nelts == 0
3750 || !SVN_IS_VALID_REVNUM(n_range.start))
3752 /* No valid information for an header */
3753 return SVN_NO_ERROR;
3756 notify_abspath = child->abspath;
3760 if (merge_b->notify_begin.last_abspath)
3761 return SVN_NO_ERROR; /* already notified */
3763 notify_abspath = merge_b->target->abspath;
3764 /* Store something in last_abspath. Any value would do */
3765 merge_b->notify_begin.last_abspath = merge_b->target->abspath;
3768 notify = svn_wc_create_notify(notify_abspath,
3770 ? svn_wc_notify_merge_begin
3771 : svn_wc_notify_foreign_merge_begin,
3774 if (SVN_IS_VALID_REVNUM(n_range.start))
3776 /* If the merge source has a gap, then don't mention
3777 those gap revisions in the notification. */
3778 remove_source_gap(&n_range, merge_b->implicit_src_gap);
3779 notify->merge_range = &n_range;
3783 notify->merge_range = NULL;
3786 merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
3789 return SVN_NO_ERROR;
3792 /* Set *OUT_RANGELIST to the intersection of IN_RANGELIST with the simple
3793 * (inheritable) revision range REV1:REV2, according to CONSIDER_INHERITANCE.
3794 * If REV1 is equal to REV2, the result is an empty rangelist, otherwise
3795 * REV1 must be less than REV2.
3797 * Note: If CONSIDER_INHERITANCE is FALSE, the effect is to treat any non-
3798 * inheritable input ranges as if they were inheritable. If it is TRUE, the
3799 * effect is to discard any non-inheritable input ranges. Therefore the
3800 * ranges in *OUT_RANGELIST will always be inheritable. */
3801 static svn_error_t *
3802 rangelist_intersect_range(svn_rangelist_t **out_rangelist,
3803 const svn_rangelist_t *in_rangelist,
3806 svn_boolean_t consider_inheritance,
3807 apr_pool_t *result_pool,
3808 apr_pool_t *scratch_pool)
3810 SVN_ERR_ASSERT(rev1 <= rev2);
3814 svn_rangelist_t *simple_rangelist =
3815 svn_rangelist__initialize(rev1, rev2, TRUE, scratch_pool);
3817 SVN_ERR(svn_rangelist_intersect(out_rangelist,
3818 simple_rangelist, in_rangelist,
3819 consider_inheritance, result_pool));
3823 *out_rangelist = apr_array_make(result_pool, 0,
3824 sizeof(svn_merge_range_t *));
3826 return SVN_NO_ERROR;
3829 /* Helper for fix_deleted_subtree_ranges(). Like fix_deleted_subtree_ranges()
3830 this function should only be called when honoring mergeinfo.
3832 CHILD, PARENT, REVISION1, REVISION2, and RA_SESSION are all cascaded from
3833 fix_deleted_subtree_ranges() -- see that function for more information on
3836 If PARENT is not the merge target then PARENT must have already have been
3837 processed by this function as a child. Specifically, this means that
3838 PARENT->REMAINING_RANGES must already be populated -- it can be an empty
3839 rangelist but cannot be NULL.
3841 PRIMARY_URL is the merge source url of CHILD at the younger of REVISION1
3844 Since this function is only invoked for subtrees of the merge target, the
3845 guarantees afforded by normalize_merge_sources() don't apply - see the
3846 'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file.
3847 Therefore it is possible that PRIMARY_URL@REVISION1 and
3848 PRIMARY_URL@REVISION2 don't describe the endpoints of an unbroken line of
3849 history. The purpose of this helper is to identify these cases of broken
3850 history and adjust CHILD->REMAINING_RANGES in such a way we don't later try
3851 to describe nonexistent path/revisions to the merge report editor -- see
3852 drive_merge_report_editor().
3854 If PRIMARY_URL@REVISION1 and PRIMARY_URL@REVISION2 describe an unbroken
3855 line of history then do nothing and leave CHILD->REMAINING_RANGES as-is.
3857 If neither PRIMARY_URL@REVISION1 nor PRIMARY_URL@REVISION2 exist then
3858 there is nothing to merge to CHILD->ABSPATH so set CHILD->REMAINING_RANGES
3859 equal to PARENT->REMAINING_RANGES. This will cause the subtree to
3860 effectively ignore CHILD -- see 'Note: If the first svn_merge_range_t...'
3861 in drive_merge_report_editor()'s doc string.
3863 If PRIMARY_URL@REVISION1 *xor* PRIMARY_URL@REVISION2 exist then we take the
3864 subset of REVISION1:REVISION2 in CHILD->REMAINING_RANGES at which
3865 PRIMARY_URL doesn't exist and set that subset equal to
3866 PARENT->REMAINING_RANGES' intersection with that non-existent range. Why?
3867 Because this causes CHILD->REMAINING_RANGES to be identical to
3868 PARENT->REMAINING_RANGES for revisions between REVISION1 and REVISION2 at
3869 which PRIMARY_URL doesn't exist. As mentioned above this means that
3870 drive_merge_report_editor() won't attempt to describe these non-existent
3871 subtree path/ranges to the reporter (which would break the merge).
3873 If the preceding paragraph wasn't terribly clear then what follows spells
3874 out this function's behavior a bit more explicitly:
3876 For forward merges (REVISION1 < REVISION2)
3878 If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then
3879 find the revision 'N' in which PRIMARY_URL@REVISION1 was deleted. Leave
3880 the subset of CHILD->REMAINING_RANGES that intersects with
3881 REVISION1:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3882 that intersects with (N - 1):REVISION2 equal to PARENT->REMAINING_RANGES'
3883 intersection with (N - 1):REVISION2.
3885 If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does,
3886 then find the revision 'M' in which PRIMARY_URL@REVISION2 came into
3887 existence. Leave the subset of CHILD->REMAINING_RANGES that intersects with
3888 (M - 1):REVISION2 as-is and set the subset of CHILD->REMAINING_RANGES
3889 that intersects with REVISION1:(M - 1) equal to PARENT->REMAINING_RANGES'
3890 intersection with REVISION1:(M - 1).
3892 For reverse merges (REVISION1 > REVISION2)
3894 If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then
3895 find the revision 'N' in which PRIMARY_URL@REVISION1 came into existence.
3896 Leave the subset of CHILD->REMAINING_RANGES that intersects with
3897 REVISION2:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3898 that intersects with (N - 1):REVISION1 equal to PARENT->REMAINING_RANGES'
3899 intersection with (N - 1):REVISION1.
3901 If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does,
3902 then find the revision 'M' in which PRIMARY_URL@REVISION2 came into
3903 existence. Leave the subset of CHILD->REMAINING_RANGES that intersects with
3904 REVISION2:(M - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3905 that intersects with (M - 1):REVISION1 equal to PARENT->REMAINING_RANGES'
3906 intersection with REVISION1:(M - 1).
3908 SCRATCH_POOL is used for all temporary allocations. Changes to CHILD are
3909 allocated in RESULT_POOL. */
3910 static svn_error_t *
3911 adjust_deleted_subtree_ranges(svn_client__merge_path_t *child,
3912 svn_client__merge_path_t *parent,
3913 svn_revnum_t revision1,
3914 svn_revnum_t revision2,
3915 const char *primary_url,
3916 svn_ra_session_t *ra_session,
3917 svn_client_ctx_t *ctx,
3918 apr_pool_t *result_pool,
3919 apr_pool_t *scratch_pool)
3921 svn_boolean_t is_rollback = revision2 < revision1;
3922 svn_revnum_t younger_rev = is_rollback ? revision1 : revision2;
3923 svn_revnum_t peg_rev = younger_rev;
3924 svn_revnum_t older_rev = is_rollback ? revision2 : revision1;
3925 apr_array_header_t *segments;
3928 SVN_ERR_ASSERT(parent->remaining_ranges);
3930 err = svn_client__repos_location_segments(&segments, ra_session,
3931 primary_url, peg_rev,
3932 younger_rev, older_rev, ctx,
3937 const char *rel_source_path; /* PRIMARY_URL relative to RA_SESSION */
3938 svn_node_kind_t kind;
3940 if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
3941 return svn_error_trace(err);
3943 svn_error_clear(err);
3945 /* PRIMARY_URL@peg_rev doesn't exist. Check if PRIMARY_URL@older_rev
3946 exists, if neither exist then the editor can simply ignore this
3949 SVN_ERR(svn_ra_get_path_relative_to_session(
3950 ra_session, &rel_source_path, primary_url, scratch_pool));
3952 SVN_ERR(svn_ra_check_path(ra_session, rel_source_path,
3953 older_rev, &kind, scratch_pool));
3954 if (kind == svn_node_none)
3956 /* Neither PRIMARY_URL@peg_rev nor PRIMARY_URL@older_rev exist,
3957 so there is nothing to merge. Set CHILD->REMAINING_RANGES
3958 identical to PARENT's. */
3959 child->remaining_ranges =
3960 svn_rangelist_dup(parent->remaining_ranges, scratch_pool);
3964 svn_rangelist_t *deleted_rangelist;
3965 svn_revnum_t rev_primary_url_deleted;
3967 /* PRIMARY_URL@older_rev exists, so it was deleted at some
3968 revision prior to peg_rev, find that revision. */
3969 SVN_ERR(svn_ra_get_deleted_rev(ra_session, rel_source_path,
3970 older_rev, younger_rev,
3971 &rev_primary_url_deleted,
3974 /* PRIMARY_URL@older_rev exists and PRIMARY_URL@peg_rev doesn't,
3975 so svn_ra_get_deleted_rev() should always find the revision
3976 PRIMARY_URL@older_rev was deleted. */
3977 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev_primary_url_deleted));
3979 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
3980 PARENT->REMAINING_RANGES so both will work with the
3981 svn_rangelist_* APIs below. */
3984 /* svn_rangelist_reverse operates in place so it's safe
3985 to use our scratch_pool. */
3986 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
3988 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
3992 /* Find the intersection of CHILD->REMAINING_RANGES with the
3993 range over which PRIMARY_URL@older_rev exists (ending at
3994 the youngest revision at which it still exists). */
3995 SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
3996 child->remaining_ranges,
3998 rev_primary_url_deleted - 1,
4000 scratch_pool, scratch_pool));
4002 /* Merge into CHILD->REMAINING_RANGES the intersection of
4003 PARENT->REMAINING_RANGES with the range beginning when
4004 PRIMARY_URL@older_rev was deleted until younger_rev. */
4005 SVN_ERR(rangelist_intersect_range(&deleted_rangelist,
4006 parent->remaining_ranges,
4007 rev_primary_url_deleted - 1,
4010 scratch_pool, scratch_pool));
4011 SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
4012 deleted_rangelist, scratch_pool,
4015 /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
4016 to reverse order if necessary. */
4019 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
4021 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
4026 else /* PRIMARY_URL@peg_rev exists. */
4028 svn_rangelist_t *non_existent_rangelist;
4029 svn_location_segment_t *segment =
4030 APR_ARRAY_IDX(segments, (segments->nelts - 1),
4031 svn_location_segment_t *);
4033 /* We know PRIMARY_URL@peg_rev exists as the call to
4034 svn_client__repos_location_segments() succeeded. If there is only
4035 one segment that starts at oldest_rev then we know that
4036 PRIMARY_URL@oldest_rev:PRIMARY_URL@peg_rev describes an unbroken
4037 line of history, so there is nothing more to adjust in
4038 CHILD->REMAINING_RANGES. */
4039 if (segment->range_start == older_rev)
4041 return SVN_NO_ERROR;
4044 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
4045 PARENT->REMAINING_RANGES so both will work with the
4046 svn_rangelist_* APIs below. */
4049 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
4051 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
4055 /* Intersect CHILD->REMAINING_RANGES with the range where PRIMARY_URL
4056 exists. Since segment doesn't span older_rev:peg_rev we know
4057 PRIMARY_URL@peg_rev didn't come into existence until
4058 segment->range_start + 1. */
4059 SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
4060 child->remaining_ranges,
4061 segment->range_start, peg_rev,
4062 FALSE, scratch_pool, scratch_pool));
4064 /* Merge into CHILD->REMAINING_RANGES the intersection of
4065 PARENT->REMAINING_RANGES with the range before PRIMARY_URL@peg_rev
4066 came into existence. */
4067 SVN_ERR(rangelist_intersect_range(&non_existent_rangelist,
4068 parent->remaining_ranges,
4069 older_rev, segment->range_start,
4070 FALSE, scratch_pool, scratch_pool));
4071 SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
4072 non_existent_rangelist, scratch_pool,
4075 /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
4076 to reverse order if necessary. */
4079 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
4081 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
4086 /* Make a lasting copy of CHILD->REMAINING_RANGES using POOL. */
4087 child->remaining_ranges = svn_rangelist_dup(child->remaining_ranges,
4089 return SVN_NO_ERROR;
4092 /* Helper for do_directory_merge().
4094 SOURCE is cascaded from the argument of the same name in
4095 do_directory_merge(). TARGET is the merge target. RA_SESSION is the
4096 session for the younger of SOURCE->loc1 and SOURCE->loc2.
4098 Adjust the subtrees in CHILDREN_WITH_MERGEINFO so that we don't
4099 later try to describe invalid paths in drive_merge_report_editor().
4100 This function is just a thin wrapper around
4101 adjust_deleted_subtree_ranges(), which see for further details.
4103 SCRATCH_POOL is used for all temporary allocations. Changes to
4104 CHILDREN_WITH_MERGEINFO are allocated in RESULT_POOL.
4106 static svn_error_t *
4107 fix_deleted_subtree_ranges(const merge_source_t *source,
4108 const merge_target_t *target,
4109 svn_ra_session_t *ra_session,
4110 apr_array_header_t *children_with_mergeinfo,
4111 svn_client_ctx_t *ctx,
4112 apr_pool_t *result_pool,
4113 apr_pool_t *scratch_pool)
4116 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4117 svn_boolean_t is_rollback = source->loc2->rev < source->loc1->rev;
4119 assert(session_url_is(ra_session,
4120 (is_rollback ? source->loc1 : source->loc2)->url,
4123 /* CHILDREN_WITH_MERGEINFO is sorted in depth-first order, so
4124 start at index 1 to examine only subtrees. */
4125 for (i = 1; i < children_with_mergeinfo->nelts; i++)
4127 svn_client__merge_path_t *child =
4128 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
4129 svn_client__merge_path_t *parent;
4130 svn_rangelist_t *deleted_rangelist, *added_rangelist;
4132 SVN_ERR_ASSERT(child);
4136 svn_pool_clear(iterpool);
4138 /* Find CHILD's parent. */
4139 parent = find_nearest_ancestor(children_with_mergeinfo,
4140 FALSE, child->abspath);
4142 /* Since CHILD is a subtree then its parent must be in
4143 CHILDREN_WITH_MERGEINFO, see the global comment
4144 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
4145 SVN_ERR_ASSERT(parent);
4147 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
4148 so it will work with the svn_rangelist_diff API. */
4151 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
4152 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
4155 SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
4156 child->remaining_ranges,
4157 parent->remaining_ranges,
4162 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
4163 SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
4166 /* If CHILD is the merge target we then know that SOURCE is provided
4167 by normalize_merge_sources() -- see 'MERGEINFO MERGE SOURCE
4168 NORMALIZATION'. Due to this normalization we know that SOURCE
4169 describes an unbroken line of history such that the entire range
4170 described by SOURCE can potentially be merged to CHILD.
4172 But if CHILD is a subtree we don't have the same guarantees about
4173 SOURCE as we do for the merge target. SOURCE->loc1 and/or
4174 SOURCE->loc2 might not exist.
4176 If one or both doesn't exist, then adjust CHILD->REMAINING_RANGES
4177 such that we don't later try to describe invalid subtrees in
4178 drive_merge_report_editor(), as that will break the merge.
4179 If CHILD has the same remaining ranges as PARENT however, then
4180 there is no need to make these adjustments, since
4181 drive_merge_report_editor() won't attempt to describe CHILD in this
4182 case, see the 'Note' in drive_merge_report_editor's docstring. */
4183 if (deleted_rangelist->nelts || added_rangelist->nelts)
4185 const char *child_primary_source_url;
4186 const char *child_repos_src_path =
4187 svn_dirent_is_child(target->abspath, child->abspath, iterpool);
4189 /* This loop is only processing subtrees, so CHILD->ABSPATH
4190 better be a proper child of the merge target. */
4191 SVN_ERR_ASSERT(child_repos_src_path);
4193 child_primary_source_url =
4194 svn_path_url_add_component2((source->loc1->rev < source->loc2->rev)
4195 ? source->loc2->url : source->loc1->url,
4196 child_repos_src_path, iterpool);
4198 SVN_ERR(adjust_deleted_subtree_ranges(child, parent,
4201 child_primary_source_url,
4203 ctx, result_pool, iterpool));
4207 svn_pool_destroy(iterpool);
4208 return SVN_NO_ERROR;
4211 /*-----------------------------------------------------------------------*/
4213 /*** Determining What Remains To Be Merged ***/
4215 /* Get explicit and/or implicit mergeinfo for the working copy path
4218 If RECORDED_MERGEINFO is not NULL then set *RECORDED_MERGEINFO
4219 to TARGET_ABSPATH's explicit or inherited mergeinfo as dictated by
4222 If IMPLICIT_MERGEINFO is not NULL then set *IMPLICIT_MERGEINFO
4223 to TARGET_ABSPATH's implicit mergeinfo (a.k.a. natural history).
4225 If both RECORDED_MERGEINFO and IMPLICIT_MERGEINFO are not NULL and
4226 *RECORDED_MERGEINFO is inherited, then *IMPLICIT_MERGEINFO will be
4227 removed from *RECORDED_MERGEINFO.
4229 If INHERITED is not NULL set *INHERITED to TRUE if *RECORDED_MERGEINFO
4230 is inherited rather than explicit. If RECORDED_MERGEINFO is NULL then
4231 INHERITED is ignored.
4234 If IMPLICIT_MERGEINFO is not NULL then START and END are limits on
4235 the natural history sought, must both be valid revision numbers, and
4236 START must be greater than END. If TARGET_ABSPATH's base revision
4237 is older than START, then the base revision is used as the younger
4238 bound in place of START.
4240 RA_SESSION is an RA session open to the repository in which TARGET_ABSPATH
4241 lives. It may be temporarily reparented as needed by this function.
4243 Allocate *RECORDED_MERGEINFO and *IMPLICIT_MERGEINFO in RESULT_POOL.
4244 Use SCRATCH_POOL for any temporary allocations. */
4245 static svn_error_t *
4246 get_full_mergeinfo(svn_mergeinfo_t *recorded_mergeinfo,
4247 svn_mergeinfo_t *implicit_mergeinfo,
4248 svn_boolean_t *inherited,
4249 svn_mergeinfo_inheritance_t inherit,
4250 svn_ra_session_t *ra_session,
4251 const char *target_abspath,
4254 svn_client_ctx_t *ctx,
4255 apr_pool_t *result_pool,
4256 apr_pool_t *scratch_pool)
4258 /* First, we get the real mergeinfo. */
4259 if (recorded_mergeinfo)
4261 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(recorded_mergeinfo,
4263 NULL /* from_repos */,
4265 inherit, ra_session,
4270 if (implicit_mergeinfo)
4272 svn_client__pathrev_t *target;
4274 /* Assert that we have sane input. */
4275 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start) && SVN_IS_VALID_REVNUM(end)
4278 /* Retrieve the origin (original_*) of the node, or just the
4279 url if the node was not copied. */
4280 SVN_ERR(svn_client__wc_node_get_origin(&target, target_abspath, ctx,
4281 scratch_pool, scratch_pool));
4285 /* We've been asked to operate on a locally added target, so its
4286 * implicit mergeinfo is empty. */
4287 *implicit_mergeinfo = apr_hash_make(result_pool);
4289 else if (target->rev <= end)
4291 /* We're asking about a range outside our natural history
4292 altogether. That means our implicit mergeinfo is empty. */
4293 *implicit_mergeinfo = apr_hash_make(result_pool);
4297 /* Fetch so-called "implicit mergeinfo" (that is, natural
4300 /* Do not ask for implicit mergeinfo from TARGET_ABSPATH's future.
4301 TARGET_ABSPATH might not even exist, and even if it does the
4302 working copy is *at* TARGET_REV so its implicit history ends
4304 if (target->rev < start)
4305 start = target->rev;
4307 /* Fetch the implicit mergeinfo. */
4308 SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo,
4314 } /*if (implicit_mergeinfo) */
4316 return SVN_NO_ERROR;
4319 /* Helper for ensure_implicit_mergeinfo().
4321 PARENT, CHILD, REVISION1, REVISION2 and CTX
4322 are all cascaded from the arguments of the same names in
4323 ensure_implicit_mergeinfo(). PARENT and CHILD must both exist, i.e.
4324 this function should never be called where CHILD is the merge target.
4326 If PARENT->IMPLICIT_MERGEINFO is NULL, obtain it from the server.
4328 Set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
4329 PARENT->IMPLICIT_MERGEINFO. CHILD->IMPLICIT_MERGEINFO is allocated
4332 RA_SESSION is an RA session open to the repository that contains CHILD.
4333 It may be temporarily reparented by this function.
4335 static svn_error_t *
4336 inherit_implicit_mergeinfo_from_parent(svn_client__merge_path_t *parent,
4337 svn_client__merge_path_t *child,
4338 svn_revnum_t revision1,
4339 svn_revnum_t revision2,
4340 svn_ra_session_t *ra_session,
4341 svn_client_ctx_t *ctx,
4342 apr_pool_t *result_pool,
4343 apr_pool_t *scratch_pool)
4345 const char *path_diff;
4347 /* This only works on subtrees! */
4348 SVN_ERR_ASSERT(parent);
4349 SVN_ERR_ASSERT(child);
4351 /* While PARENT must exist, it is possible we've deferred
4352 getting its implicit mergeinfo. If so get it now. */
4353 if (!parent->implicit_mergeinfo)
4354 SVN_ERR(get_full_mergeinfo(NULL, &(parent->implicit_mergeinfo),
4355 NULL, svn_mergeinfo_inherited,
4356 ra_session, child->abspath,
4357 MAX(revision1, revision2),
4358 MIN(revision1, revision2),
4359 ctx, result_pool, scratch_pool));
4361 /* Let CHILD inherit PARENT's implicit mergeinfo. */
4363 path_diff = svn_dirent_is_child(parent->abspath, child->abspath,
4365 /* PARENT->PATH better be an ancestor of CHILD->ABSPATH! */
4366 SVN_ERR_ASSERT(path_diff);
4368 SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
4369 &child->implicit_mergeinfo, parent->implicit_mergeinfo,
4370 path_diff, result_pool, scratch_pool));
4371 child->implicit_mergeinfo = svn_mergeinfo_dup(child->implicit_mergeinfo,
4373 return SVN_NO_ERROR;
4376 /* Helper of filter_merged_revisions().
4378 If we have deferred obtaining CHILD->IMPLICIT_MERGEINFO, then get
4379 it now, allocating it in RESULT_POOL. If CHILD_INHERITS_PARENT is true
4380 then set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
4381 PARENT->IMPLICIT_MERGEINFO, otherwise contact the repository. Use
4382 SCRATCH_POOL for all temporary allocations.
4384 RA_SESSION is an RA session open to the repository that contains CHILD.
4385 It may be temporarily reparented by this function.
4387 PARENT, CHILD, REVISION1, REVISION2 and
4388 CTX are all cascaded from the arguments of the same name in
4389 filter_merged_revisions() and the same conditions for that function
4391 static svn_error_t *
4392 ensure_implicit_mergeinfo(svn_client__merge_path_t *parent,
4393 svn_client__merge_path_t *child,
4394 svn_boolean_t child_inherits_parent,
4395 svn_revnum_t revision1,
4396 svn_revnum_t revision2,
4397 svn_ra_session_t *ra_session,
4398 svn_client_ctx_t *ctx,
4399 apr_pool_t *result_pool,
4400 apr_pool_t *scratch_pool)
4402 /* If we haven't already found CHILD->IMPLICIT_MERGEINFO then
4403 contact the server to get it. */
4405 if (child->implicit_mergeinfo)
4406 return SVN_NO_ERROR;
4408 if (child_inherits_parent)
4409 SVN_ERR(inherit_implicit_mergeinfo_from_parent(parent,
4418 SVN_ERR(get_full_mergeinfo(NULL,
4419 &(child->implicit_mergeinfo),
4420 NULL, svn_mergeinfo_inherited,
4421 ra_session, child->abspath,
4422 MAX(revision1, revision2),
4423 MIN(revision1, revision2),
4424 ctx, result_pool, scratch_pool));
4426 return SVN_NO_ERROR;
4429 /* Helper for calculate_remaining_ranges().
4431 Initialize CHILD->REMAINING_RANGES to a rangelist representing the
4432 requested merge of REVISION1:REVISION2 from MERGEINFO_PATH to
4435 For forward merges remove any ranges from CHILD->REMAINING_RANGES that
4436 have already been merged to CHILD->ABSPATH per TARGET_MERGEINFO or
4437 CHILD->IMPLICIT_MERGEINFO. For reverse merges remove any ranges from
4438 CHILD->REMAINING_RANGES that have not already been merged to CHILD->ABSPATH
4439 per TARGET_MERGEINFO or CHILD->IMPLICIT_MERGEINFO. If we have deferred
4440 obtaining CHILD->IMPLICIT_MERGEINFO and it is necessary to use it for
4441 these calculations, then get it from the server, allocating it in
4444 CHILD represents a working copy path which is the merge target or one of
4445 the target's subtrees. If not NULL, PARENT is CHILD's nearest path-wise
4446 ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'.
4448 If the function needs to consider CHILD->IMPLICIT_MERGEINFO and
4449 CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the
4450 mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO. Otherwise contact
4451 the repository for CHILD->IMPLICIT_MERGEINFO.
4453 NOTE: If PARENT is present then this function must have previously been
4454 called for PARENT, i.e. if populate_remaining_ranges() is calling this
4455 function for a set of svn_client__merge_path_t* the calls must be made
4456 in depth-first order.
4458 MERGEINFO_PATH is the merge source relative to the repository root.
4460 REVISION1 and REVISION2 describe the merge range requested from
4463 TARGET_RANGELIST is the portion of CHILD->ABSPATH's explicit or inherited
4464 mergeinfo that intersects with the merge history described by
4465 MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2. TARGET_RANGELIST
4466 should be NULL if there is no explicit or inherited mergeinfo on
4467 CHILD->ABSPATH or an empty list if CHILD->ABSPATH has empty mergeinfo or
4468 explicit mergeinfo that exclusively describes non-intersecting history
4469 with MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2.
4471 SCRATCH_POOL is used for all temporary allocations.
4473 NOTE: This should only be called when honoring mergeinfo.
4475 NOTE: Like calculate_remaining_ranges() if PARENT is present then this
4476 function must have previously been called for PARENT.
4478 static svn_error_t *
4479 filter_merged_revisions(svn_client__merge_path_t *parent,
4480 svn_client__merge_path_t *child,
4481 const char *mergeinfo_path,
4482 svn_rangelist_t *target_rangelist,
4483 svn_revnum_t revision1,
4484 svn_revnum_t revision2,
4485 svn_boolean_t child_inherits_implicit,
4486 svn_ra_session_t *ra_session,
4487 svn_client_ctx_t *ctx,
4488 apr_pool_t *result_pool,
4489 apr_pool_t *scratch_pool)
4491 svn_rangelist_t *requested_rangelist,
4492 *target_implicit_rangelist, *explicit_rangelist;
4494 /* Convert REVISION1 and REVISION2 to a rangelist.
4496 Note: Talking about a requested merge range's inheritability
4497 doesn't make much sense, but as we are using svn_merge_range_t
4498 to describe it we need to pick *something*. Since all the
4499 rangelist manipulations in this function either don't consider
4500 inheritance by default or we are requesting that they don't (i.e.
4501 svn_rangelist_remove and svn_rangelist_intersect) then we could
4502 set the inheritability as FALSE, it won't matter either way. */
4503 requested_rangelist = svn_rangelist__initialize(revision1, revision2,
4504 TRUE, scratch_pool);
4506 /* Now filter out revisions that have already been merged to CHILD. */
4508 if (revision1 > revision2) /* This is a reverse merge. */
4510 svn_rangelist_t *added_rangelist, *deleted_rangelist;
4512 /* The revert range and will need to be reversed for
4513 our svn_rangelist_* APIs to work properly. */
4514 SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
4516 /* Set EXPLICIT_RANGELIST to the list of source-range revs that are
4517 already recorded as merged to target. */
4518 if (target_rangelist)
4520 /* Return the intersection of the revs which are both already
4521 represented by CHILD's explicit or inherited mergeinfo.
4523 We don't consider inheritance when determining intersecting
4524 ranges. If we *did* consider inheritance, then our calculation
4525 would be wrong. For example, if the CHILD->REMAINING_RANGES is
4526 5:3 and TARGET_RANGELIST is r5* (non-inheritable) then the
4527 intersection would be r4. And that would be wrong as we clearly
4528 want to reverse merge both r4 and r5 in this case. Ignoring the
4529 ranges' inheritance results in an intersection of r4-5.
4531 You might be wondering about CHILD's children, doesn't the above
4532 imply that we will reverse merge r4-5 from them? Nope, this is
4533 safe to do because any path whose parent has non-inheritable
4534 ranges is always considered a subtree with differing mergeinfo
4535 even if that path has no explicit mergeinfo prior to the
4536 merge -- See condition 3 in the doc string for
4537 merge.c:get_mergeinfo_paths(). */
4538 SVN_ERR(svn_rangelist_intersect(&explicit_rangelist,
4540 requested_rangelist,
4541 FALSE, scratch_pool));
4545 explicit_rangelist =
4546 apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *));
4549 /* Was any part of the requested reverse merge not accounted for in
4550 CHILD's explicit or inherited mergeinfo? */
4551 SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
4552 requested_rangelist, explicit_rangelist,
4553 FALSE, scratch_pool));
4555 if (deleted_rangelist->nelts == 0)
4557 /* The whole of REVISION1:REVISION2 was represented in CHILD's
4558 explicit/inherited mergeinfo, allocate CHILD's remaining
4559 ranges in POOL and then we are done. */
4560 SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
4561 child->remaining_ranges = svn_rangelist_dup(requested_rangelist,
4564 else /* We need to check CHILD's implicit mergeinfo. */
4566 svn_rangelist_t *implicit_rangelist;
4568 SVN_ERR(ensure_implicit_mergeinfo(parent,
4570 child_inherits_implicit,
4578 target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo,
4581 if (target_implicit_rangelist)
4582 SVN_ERR(svn_rangelist_intersect(&implicit_rangelist,
4583 target_implicit_rangelist,
4584 requested_rangelist,
4585 FALSE, scratch_pool));
4587 implicit_rangelist = apr_array_make(scratch_pool, 0,
4588 sizeof(svn_merge_range_t *));
4590 SVN_ERR(svn_rangelist_merge2(implicit_rangelist,
4591 explicit_rangelist, scratch_pool,
4593 SVN_ERR(svn_rangelist_reverse(implicit_rangelist, scratch_pool));
4594 child->remaining_ranges = svn_rangelist_dup(implicit_rangelist,
4598 else /* This is a forward merge */
4600 /* Set EXPLICIT_RANGELIST to the list of source-range revs that are
4601 NOT already recorded as merged to target. */
4602 if (target_rangelist)
4604 /* See earlier comment preceding svn_rangelist_intersect() for
4605 why we don't consider inheritance here. */
4606 SVN_ERR(svn_rangelist_remove(&explicit_rangelist,
4608 requested_rangelist, FALSE,
4613 explicit_rangelist = svn_rangelist_dup(requested_rangelist,
4617 if (explicit_rangelist->nelts == 0)
4619 child->remaining_ranges =
4620 apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *));
4623 /* ### TODO: Which evil shall we choose?
4625 ### If we allow all forward-merges not already found in recorded
4626 ### mergeinfo, we destroy the ability to, say, merge the whole of a
4627 ### branch to the trunk while automatically ignoring the revisions
4628 ### common to both. That's bad.
4630 ### If we allow only forward-merges not found in either recorded
4631 ### mergeinfo or implicit mergeinfo (natural history), then the
4632 ### previous scenario works great, but we can't reverse-merge a
4633 ### previous change made to our line of history and then remake it
4634 ### (because the reverse-merge will leave no mergeinfo trace, and
4635 ### the remake-it attempt will still find the original change in
4636 ### natural mergeinfo. But you know, that we happen to use 'merge'
4637 ### for revision undoing is somewhat unnatural anyway, so I'm
4638 ### finding myself having little interest in caring too much about
4639 ### this. That said, if we had a way of storing reverse merge
4640 ### ranges, we'd be in good shape either way.
4642 #ifdef SVN_MERGE__ALLOW_ALL_FORWARD_MERGES_FROM_SELF
4644 /* ### Don't consider implicit mergeinfo. */
4645 child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
4650 /* Based on CHILD's TARGET_MERGEINFO there are ranges to merge.
4651 Check CHILD's implicit mergeinfo to see if these remaining
4652 ranges are represented there. */
4653 SVN_ERR(ensure_implicit_mergeinfo(parent,
4655 child_inherits_implicit,
4663 target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo,
4665 if (target_implicit_rangelist)
4666 SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
4667 target_implicit_rangelist,
4669 FALSE, result_pool));
4671 child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
4677 return SVN_NO_ERROR;
4680 /* Helper for do_file_merge and do_directory_merge (by way of
4681 populate_remaining_ranges() for the latter).
4683 Determine what portions of SOURCE have already
4684 been merged to CHILD->ABSPATH and populate CHILD->REMAINING_RANGES with
4685 the ranges that still need merging.
4687 SOURCE and CTX are all cascaded from the caller's arguments of the same
4688 names. Note that this means SOURCE adheres to the requirements noted in
4689 `MERGEINFO MERGE SOURCE NORMALIZATION'.
4691 CHILD represents a working copy path which is the merge target or one of
4692 the target's subtrees. If not NULL, PARENT is CHILD's nearest path-wise
4693 ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. TARGET_MERGEINFO is
4694 the working mergeinfo on CHILD.
4696 RA_SESSION is the session for the younger of SOURCE->loc1 and
4699 If the function needs to consider CHILD->IMPLICIT_MERGEINFO and
4700 CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the
4701 mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO. Otherwise contact
4702 the repository for CHILD->IMPLICIT_MERGEINFO.
4704 If not null, IMPLICIT_SRC_GAP is the gap, if any, in the natural history
4705 of SOURCE, see merge_cmd_baton_t.implicit_src_gap.
4707 SCRATCH_POOL is used for all temporary allocations. Changes to CHILD and
4708 PARENT are made in RESULT_POOL.
4710 NOTE: This should only be called when honoring mergeinfo.
4712 NOTE: If PARENT is present then this function must have previously been
4713 called for PARENT, i.e. if populate_remaining_ranges() is calling this
4714 function for a set of svn_client__merge_path_t* the calls must be made
4715 in depth-first order.
4717 NOTE: When performing reverse merges, return
4718 SVN_ERR_CLIENT_NOT_READY_TO_MERGE if both locations in SOURCE and
4719 CHILD->ABSPATH are all on the same line of history but CHILD->ABSPATH's
4720 base revision is older than the SOURCE->rev1:rev2 range, see comment re
4723 static svn_error_t *
4724 calculate_remaining_ranges(svn_client__merge_path_t *parent,
4725 svn_client__merge_path_t *child,
4726 const merge_source_t *source,
4727 svn_mergeinfo_t target_mergeinfo,
4728 const apr_array_header_t *implicit_src_gap,
4729 svn_boolean_t child_inherits_implicit,
4730 svn_ra_session_t *ra_session,
4731 svn_client_ctx_t *ctx,
4732 apr_pool_t *result_pool,
4733 apr_pool_t *scratch_pool)
4735 const svn_client__pathrev_t *primary_src
4736 = (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1;
4737 const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
4739 /* Intersection of TARGET_MERGEINFO and the merge history
4740 described by SOURCE. */
4741 svn_rangelist_t *target_rangelist;
4742 svn_revnum_t child_base_revision;
4744 /* Since this function should only be called when honoring mergeinfo and
4745 * SOURCE adheres to the requirements noted in 'MERGEINFO MERGE SOURCE
4746 * NORMALIZATION', SOURCE must be 'ancestral'. */
4747 SVN_ERR_ASSERT(source->ancestral);
4749 /* Determine which of the requested ranges to consider merging... */
4751 /* Set TARGET_RANGELIST to the portion of TARGET_MERGEINFO that refers
4752 to SOURCE (excluding any gap in SOURCE): first get all ranges from
4753 TARGET_MERGEINFO that refer to the path of SOURCE, and then prune
4754 any ranges that lie in the gap in SOURCE.
4756 ### [JAF] In fact, that may still leave some ranges that lie entirely
4757 outside the range of SOURCE; it seems we don't care about that. */
4758 if (target_mergeinfo)
4759 target_rangelist = svn_hash_gets(target_mergeinfo, mergeinfo_path);
4761 target_rangelist = NULL;
4762 if (implicit_src_gap && target_rangelist)
4764 /* Remove any mergeinfo referring to the 'gap' in SOURCE, as that
4765 mergeinfo doesn't really refer to SOURCE at all but instead
4766 refers to locations that are non-existent or on a different
4767 line of history. (Issue #3242.) */
4768 SVN_ERR(svn_rangelist_remove(&target_rangelist,
4769 implicit_src_gap, target_rangelist,
4770 FALSE, result_pool));
4773 /* Initialize CHILD->REMAINING_RANGES and filter out revisions already
4774 merged (or, in the case of reverse merges, ranges not yet merged). */
4775 SVN_ERR(filter_merged_revisions(parent, child, mergeinfo_path,
4777 source->loc1->rev, source->loc2->rev,
4778 child_inherits_implicit,
4779 ra_session, ctx, result_pool,
4782 /* Issue #2973 -- from the continuing series of "Why, since the advent of
4783 merge tracking, allowing merges into mixed rev and locally modified
4784 working copies isn't simple and could be considered downright evil".
4786 If reverse merging a range to the WC path represented by CHILD, from
4787 that path's own history, where the path inherits no locally modified
4788 mergeinfo from its WC parents (i.e. there is no uncommitted merge to
4789 the WC), and the path's base revision is older than the range, then
4790 the merge will always be a no-op. This is because we only allow reverse
4791 merges of ranges in the path's explicit or natural mergeinfo and a
4792 reverse merge from the path's future history obviously isn't going to be
4793 in either, hence the no-op.
4795 The problem is two-fold. First, in a mixed rev WC, the change we
4796 want to revert might actually be to some child of the target path
4797 which is at a younger base revision. Sure, we can merge directly
4798 to that child or update the WC or even use --ignore-ancestry and then
4799 successfully run the reverse merge, but that gets to the second
4800 problem: Those courses of action are not very obvious. Before 1.5 if
4801 a user committed a change that didn't touch the commit target, then
4802 immediately decided to revert that change via a reverse merge it would
4803 just DTRT. But with the advent of merge tracking the user gets a no-op.
4805 So in the name of user friendliness, return an error suggesting a helpful
4808 SVN_ERR(svn_wc__node_get_base(NULL, &child_base_revision,
4809 NULL, NULL, NULL, NULL,
4810 ctx->wc_ctx, child->abspath,
4811 TRUE /* ignore_enoent */,
4812 scratch_pool, scratch_pool));
4813 /* If CHILD has no base revision then it hasn't been committed yet, so it
4814 can't have any "future" history. */
4815 if (SVN_IS_VALID_REVNUM(child_base_revision)
4816 && ((child->remaining_ranges)->nelts == 0) /* Inoperative merge */
4817 && (source->loc2->rev < source->loc1->rev) /* Reverse merge */
4818 && (child_base_revision <= source->loc2->rev)) /* From CHILD's future */
4820 /* Hmmm, an inoperative reverse merge from the "future". If it is
4821 from our own future return a helpful error. */
4823 svn_client__pathrev_t *start_loc;
4825 err = svn_client__repos_location(&start_loc,
4828 child_base_revision,
4829 ctx, scratch_pool, scratch_pool);
4832 if (err->apr_err == SVN_ERR_FS_NOT_FOUND
4833 || err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
4834 svn_error_clear(err);
4836 return svn_error_trace(err);
4842 SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, child->abspath,
4843 scratch_pool, scratch_pool));
4844 if (strcmp(start_loc->url, url) == 0)
4845 return svn_error_create(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
4846 _("Cannot reverse-merge a range from a "
4847 "path's own future history; try "
4852 return SVN_NO_ERROR;
4855 /* Helper for populate_remaining_ranges().
4857 SOURCE is cascaded from the arguments of the same name in
4858 populate_remaining_ranges().
4860 Note: The following comments assume a forward merge, i.e.
4861 SOURCE->loc1->rev < SOURCE->loc2->rev. If this is a reverse merge then
4862 all the following comments still apply, but with SOURCE->loc1 switched
4865 Like populate_remaining_ranges(), SOURCE must adhere to the restrictions
4866 documented in 'MERGEINFO MERGE SOURCE NORMALIZATION'. These restrictions
4867 allow for a *single* gap in SOURCE, GAP_REV1:GAP_REV2 exclusive:inclusive
4868 (where SOURCE->loc1->rev == GAP_REV1 <= GAP_REV2 < SOURCE->loc2->rev),
4869 if SOURCE->loc2->url@(GAP_REV2+1) was copied from SOURCE->loc1. If such
4870 a gap exists, set *GAP_START and *GAP_END to the starting and ending
4871 revisions of the gap. Otherwise set both to SVN_INVALID_REVNUM.
4873 For example, if the natural history of URL@2:URL@9 is 'trunk/:2,7-9' this
4874 would indicate that trunk@7 was copied from trunk@2. This function would
4875 return GAP_START:GAP_END of 2:6 in this case. Note that a path 'trunk'
4876 might exist at r3-6, but it would not be on the same line of history as
4879 ### GAP_START is basically redundant, as (if there is a gap at all) it is
4880 necessarily the older revision of SOURCE.
4882 RA_SESSION is an open RA session to the repository in which SOURCE lives.
4884 static svn_error_t *
4885 find_gaps_in_merge_source_history(svn_revnum_t *gap_start,
4886 svn_revnum_t *gap_end,
4887 const merge_source_t *source,
4888 svn_ra_session_t *ra_session,
4889 svn_client_ctx_t *ctx,
4890 apr_pool_t *scratch_pool)
4892 svn_mergeinfo_t implicit_src_mergeinfo;
4893 svn_revnum_t old_rev = MIN(source->loc1->rev, source->loc2->rev);
4894 const svn_client__pathrev_t *primary_src
4895 = (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1;
4896 const char *merge_src_fspath = svn_client__pathrev_fspath(primary_src,
4898 svn_rangelist_t *rangelist;
4900 SVN_ERR_ASSERT(source->ancestral);
4902 /* Start by assuming there is no gap. */
4903 *gap_start = *gap_end = SVN_INVALID_REVNUM;
4905 /* Easy out: There can't be a gap between adjacent revisions. */
4906 if (labs(source->loc1->rev - source->loc2->rev) == 1)
4907 return SVN_NO_ERROR;
4909 /* Get SOURCE as mergeinfo. */
4910 SVN_ERR(svn_client__get_history_as_mergeinfo(&implicit_src_mergeinfo, NULL,
4912 primary_src->rev, old_rev,
4914 ctx, scratch_pool));
4916 rangelist = svn_hash_gets(implicit_src_mergeinfo, merge_src_fspath);
4918 if (!rangelist) /* ### Can we ever not find a rangelist? */
4919 return SVN_NO_ERROR;
4921 /* A gap in natural history can result from either a copy or
4922 a rename. If from a copy then history as mergeinfo will look
4923 something like this:
4927 If from a rename it will look like this:
4930 '/trunk_new_name:Y-Z'
4932 In both cases the gap, if it exists, is M-N, where M = X + 1 and
4935 Note that per the rules of 'MERGEINFO MERGE SOURCE NORMALIZATION' we
4936 should never have multiple gaps, e.g. if we see anything like the
4937 following then something is quite wrong:
4939 '/trunk_old_name:A,B-C'
4940 '/trunk_new_name:D-E'
4943 if (rangelist->nelts > 1) /* Copy */
4945 const svn_merge_range_t *gap;
4946 /* As mentioned above, multiple gaps *shouldn't* be possible. */
4947 SVN_ERR_ASSERT(apr_hash_count(implicit_src_mergeinfo) == 1);
4949 gap = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
4950 const svn_merge_range_t *);
4952 *gap_start = MIN(source->loc1->rev, source->loc2->rev);
4953 *gap_end = gap->start;
4956 ### This assertion triggers in merge_tests.py svnmucc_abuse_1()
4957 ### when a node is replaced by an older copy of itself.
4959 BH: I think we should review this and the 'rename' case to find
4960 out which behavior we really want, and if we can really
4961 determine what happened this way. */
4962 SVN_ERR_ASSERT(*gap_start < *gap_end);
4964 else if (apr_hash_count(implicit_src_mergeinfo) > 1) /* Rename */
4966 svn_rangelist_t *requested_rangelist =
4967 svn_rangelist__initialize(MIN(source->loc1->rev, source->loc2->rev),
4968 MAX(source->loc1->rev, source->loc2->rev),
4969 TRUE, scratch_pool);
4970 svn_rangelist_t *implicit_rangelist =
4971 apr_array_make(scratch_pool, 2, sizeof(svn_merge_range_t *));
4972 svn_rangelist_t *gap_rangelist;
4974 SVN_ERR(svn_rangelist__merge_many(implicit_rangelist,
4975 implicit_src_mergeinfo,
4976 scratch_pool, scratch_pool));
4977 SVN_ERR(svn_rangelist_remove(&gap_rangelist, implicit_rangelist,
4978 requested_rangelist, FALSE,
4981 /* If there is anything left it is the gap. */
4982 if (gap_rangelist->nelts)
4984 svn_merge_range_t *gap_range =
4985 APR_ARRAY_IDX(gap_rangelist, 0, svn_merge_range_t *);
4987 *gap_start = gap_range->start;
4988 *gap_end = gap_range->end;
4992 SVN_ERR_ASSERT(*gap_start == MIN(source->loc1->rev, source->loc2->rev)
4993 || (*gap_start == SVN_INVALID_REVNUM
4994 && *gap_end == SVN_INVALID_REVNUM));
4995 return SVN_NO_ERROR;
4998 /* Helper for do_directory_merge().
5000 For each (svn_client__merge_path_t *) child in CHILDREN_WITH_MERGEINFO,
5001 populate that child's 'remaining_ranges' list with (### ... what?),
5002 and populate that child's 'implicit_mergeinfo' with its implicit
5003 mergeinfo (natural history). CHILDREN_WITH_MERGEINFO is expected
5004 to be sorted in depth first order and each child must be processed in
5005 that order. The inheritability of all calculated ranges is TRUE.
5007 If mergeinfo is being honored (based on MERGE_B -- see HONOR_MERGEINFO()
5008 for how this is determined), this function will actually try to be
5009 intelligent about populating remaining_ranges list. Otherwise, it
5010 will claim that each child has a single remaining range, from
5011 SOURCE->rev1, to SOURCE->rev2.
5012 ### We also take the short-cut if doing record-only. Why?
5014 SCRATCH_POOL is used for all temporary allocations. Changes to
5015 CHILDREN_WITH_MERGEINFO are made in RESULT_POOL.
5017 Note that if SOURCE->rev1 > SOURCE->rev2, then each child's remaining_ranges
5018 member does not adhere to the API rules for rangelists described in
5019 svn_mergeinfo.h -- See svn_client__merge_path_t.
5021 See `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements
5024 static svn_error_t *
5025 populate_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
5026 const merge_source_t *source,
5027 svn_ra_session_t *ra_session,
5028 merge_cmd_baton_t *merge_b,
5029 apr_pool_t *result_pool,
5030 apr_pool_t *scratch_pool)
5032 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5034 svn_revnum_t gap_start, gap_end;
5036 /* If we aren't honoring mergeinfo or this is a --record-only merge,
5037 we'll make quick work of this by simply adding dummy SOURCE->rev1:rev2
5038 ranges for all children. */
5039 if (! HONOR_MERGEINFO(merge_b) || merge_b->record_only)
5041 for (i = 0; i < children_with_mergeinfo->nelts; i++)
5043 svn_client__merge_path_t *child =
5044 APR_ARRAY_IDX(children_with_mergeinfo, i,
5045 svn_client__merge_path_t *);
5047 svn_pool_clear(iterpool);
5049 /* Issue #3646 'record-only merges create self-referential
5050 mergeinfo'. Get the merge target's implicit mergeinfo (natural
5051 history). We'll use it later to avoid setting self-referential
5052 mergeinfo -- see filter_natural_history_from_mergeinfo(). */
5053 if (i == 0) /* First item is always the merge target. */
5055 SVN_ERR(get_full_mergeinfo(NULL, /* child->pre_merge_mergeinfo */
5056 &(child->implicit_mergeinfo),
5057 NULL, /* child->inherited_mergeinfo */
5058 svn_mergeinfo_inherited, ra_session,
5060 MAX(source->loc1->rev,
5062 MIN(source->loc1->rev,
5064 merge_b->ctx, result_pool,
5069 /* Issue #3443 - Subtrees of the merge target can inherit
5070 their parent's implicit mergeinfo in most cases. */
5071 svn_client__merge_path_t *parent
5072 = find_nearest_ancestor(children_with_mergeinfo,
5073 FALSE, child->abspath);
5074 svn_boolean_t child_inherits_implicit;
5076 /* If CHILD is a subtree then its parent must be in
5077 CHILDREN_WITH_MERGEINFO, see the global comment
5078 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
5079 SVN_ERR_ASSERT(parent);
5081 child_inherits_implicit = (parent && !child->switched);
5082 SVN_ERR(ensure_implicit_mergeinfo(parent, child,
5083 child_inherits_implicit,
5086 ra_session, merge_b->ctx,
5087 result_pool, iterpool));
5090 child->remaining_ranges = svn_rangelist__initialize(source->loc1->rev,
5095 svn_pool_destroy(iterpool);
5096 return SVN_NO_ERROR;
5099 /* If, in the merge source's history, there was a copy from an older
5100 revision, then SOURCE->loc2->url won't exist at some range M:N, where
5101 SOURCE->loc1->rev < M < N < SOURCE->loc2->rev. The rules of 'MERGEINFO
5102 MERGE SOURCE NORMALIZATION' allow this, but we must ignore these gaps
5103 when calculating what ranges remain to be merged from SOURCE. If we
5104 don't and try to merge any part of SOURCE->loc2->url@M:N we would
5105 break the editor since no part of that actually exists. See
5106 http://svn.haxx.se/dev/archive-2008-11/0618.shtml.
5108 Find the gaps in the merge target's history, if any. Eventually
5109 we will adjust CHILD->REMAINING_RANGES such that we don't describe
5110 non-existent paths to the editor. */
5111 SVN_ERR(find_gaps_in_merge_source_history(&gap_start, &gap_end,
5113 ra_session, merge_b->ctx,
5116 /* Stash any gap in the merge command baton, we'll need it later when
5117 recording mergeinfo describing this merge. */
5118 if (SVN_IS_VALID_REVNUM(gap_start) && SVN_IS_VALID_REVNUM(gap_end))
5119 merge_b->implicit_src_gap = svn_rangelist__initialize(gap_start, gap_end,
5122 for (i = 0; i < children_with_mergeinfo->nelts; i++)
5124 svn_client__merge_path_t *child =
5125 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5126 const char *child_repos_path
5127 = svn_dirent_skip_ancestor(merge_b->target->abspath, child->abspath);
5128 merge_source_t child_source;
5129 svn_client__merge_path_t *parent = NULL;
5130 svn_boolean_t child_inherits_implicit;
5132 svn_pool_clear(iterpool);
5134 /* If the path is absent don't do subtree merge either. */
5135 SVN_ERR_ASSERT(child);
5139 SVN_ERR_ASSERT(child_repos_path != NULL);
5140 child_source.loc1 = svn_client__pathrev_join_relpath(
5141 source->loc1, child_repos_path, iterpool);
5142 child_source.loc2 = svn_client__pathrev_join_relpath(
5143 source->loc2, child_repos_path, iterpool);
5144 /* ### Is the child 'ancestral' over the same revision range? It's
5145 * not necessarily true that a child is 'ancestral' if the parent is,
5146 * nor that it's not if the parent is not. However, here we claim
5147 * that it is. Before we had this 'ancestral' field that we need to
5148 * set explicitly, the claim was implicit. Either way, the impact is
5149 * that we might pass calculate_remaining_ranges() a source that is
5150 * not in fact 'ancestral' (despite its 'ancestral' field being true),
5151 * contrary to its doc-string. */
5152 child_source.ancestral = source->ancestral;
5154 /* Get the explicit/inherited mergeinfo for CHILD. If CHILD is the
5155 merge target then also get its implicit mergeinfo. Otherwise defer
5156 this until we know it is absolutely necessary, since it requires an
5157 expensive round trip communication with the server. */
5158 SVN_ERR(get_full_mergeinfo(
5159 child->pre_merge_mergeinfo ? NULL : &(child->pre_merge_mergeinfo),
5160 /* Get implicit only for merge target. */
5161 (i == 0) ? &(child->implicit_mergeinfo) : NULL,
5162 &(child->inherited_mergeinfo),
5163 svn_mergeinfo_inherited, ra_session,
5165 MAX(source->loc1->rev, source->loc2->rev),
5166 MIN(source->loc1->rev, source->loc2->rev),
5167 merge_b->ctx, result_pool, iterpool));
5169 /* If CHILD isn't the merge target find its parent. */
5172 parent = find_nearest_ancestor(children_with_mergeinfo,
5173 FALSE, child->abspath);
5174 /* If CHILD is a subtree then its parent must be in
5175 CHILDREN_WITH_MERGEINFO, see the global comment
5176 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
5177 SVN_ERR_ASSERT(parent);
5180 /* Issue #3443 - Can CHILD inherit PARENT's implicit mergeinfo, saving
5181 us from having to ask the repos? The only time we can't do this is if
5182 CHILD is the merge target and so there is no PARENT to inherit from
5183 or if CHILD is the root of a switched subtree, in which case PARENT
5184 exists but is not CHILD's repository parent. */
5185 child_inherits_implicit = (parent && !child->switched);
5187 SVN_ERR(calculate_remaining_ranges(parent, child,
5189 child->pre_merge_mergeinfo,
5190 merge_b->implicit_src_gap,
5191 child_inherits_implicit,
5193 merge_b->ctx, result_pool,
5196 /* Deal with any gap in SOURCE's natural history.
5198 If the gap is a proper subset of CHILD->REMAINING_RANGES then we can
5199 safely ignore it since we won't describe this path/rev pair.
5201 If the gap exactly matches or is a superset of a range in
5202 CHILD->REMAINING_RANGES then we must remove that range so we don't
5203 attempt to describe non-existent paths via the reporter, this will
5204 break the editor and our merge.
5206 If the gap adjoins or overlaps a range in CHILD->REMAINING_RANGES
5207 then we must *add* the gap so we span the missing revisions. */
5208 if (child->remaining_ranges->nelts
5209 && merge_b->implicit_src_gap)
5212 svn_boolean_t proper_subset = FALSE;
5213 svn_boolean_t overlaps_or_adjoins = FALSE;
5215 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
5216 so it will work with the svn_rangelist_* APIs below. */
5217 if (source->loc1->rev > source->loc2->rev)
5218 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
5220 for (j = 0; j < child->remaining_ranges->nelts; j++)
5222 svn_merge_range_t *range
5223 = APR_ARRAY_IDX(child->remaining_ranges, j, svn_merge_range_t *);
5225 if ((range->start <= gap_start && gap_end < range->end)
5226 || (range->start < gap_start && gap_end <= range->end))
5228 proper_subset = TRUE;
5231 else if ((gap_start == range->start) && (range->end == gap_end))
5235 else if (gap_start <= range->end && range->start <= gap_end)
5238 overlaps_or_adjoins = TRUE;
5245 /* We need to make adjustments. Remove from, or add the gap
5246 to, CHILD->REMAINING_RANGES as appropriate. */
5248 if (overlaps_or_adjoins)
5249 SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
5250 merge_b->implicit_src_gap,
5251 result_pool, iterpool));
5252 else /* equals == TRUE */
5253 SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
5254 merge_b->implicit_src_gap,
5255 child->remaining_ranges, FALSE,
5259 if (source->loc1->rev > source->loc2->rev) /* Reverse merge */
5260 SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
5264 svn_pool_destroy(iterpool);
5265 return SVN_NO_ERROR;
5269 /*-----------------------------------------------------------------------*/
5271 /*** Other Helper Functions ***/
5273 /* Calculate the new mergeinfo for the target tree rooted at TARGET_ABSPATH
5274 based on MERGES (a mapping of absolute WC paths to rangelists representing
5275 a merge from the source SOURCE_FSPATH).
5277 If RESULT_CATALOG is NULL, then record the new mergeinfo in the WC (at,
5278 and possibly below, TARGET_ABSPATH).
5280 If RESULT_CATALOG is not NULL, then don't record the new mergeinfo on the
5281 WC, but instead record it in RESULT_CATALOG, where the keys are absolute
5282 working copy paths and the values are the new mergeinfos for each.
5283 Allocate additions to RESULT_CATALOG in pool which RESULT_CATALOG was
5285 static svn_error_t *
5286 update_wc_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
5287 const char *target_abspath,
5288 const char *source_fspath,
5290 svn_boolean_t is_rollback,
5291 svn_client_ctx_t *ctx,
5292 apr_pool_t *scratch_pool)
5294 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5295 apr_hash_index_t *hi;
5297 /* Combine the mergeinfo for the revision range just merged into
5298 the WC with its on-disk mergeinfo. */
5299 for (hi = apr_hash_first(scratch_pool, merges); hi; hi = apr_hash_next(hi))
5301 const char *local_abspath = apr_hash_this_key(hi);
5302 svn_rangelist_t *ranges = apr_hash_this_val(hi);
5303 svn_rangelist_t *rangelist;
5305 const char *local_abspath_rel_to_target;
5307 svn_mergeinfo_t mergeinfo;
5309 svn_pool_clear(iterpool);
5311 /* As some of the merges may've changed the WC's mergeinfo, get
5312 a fresh copy before using it to update the WC's mergeinfo. */
5313 err = svn_client__parse_mergeinfo(&mergeinfo, ctx->wc_ctx,
5314 local_abspath, iterpool, iterpool);
5316 /* If a directory PATH was skipped because it is missing or was
5317 obstructed by an unversioned item then there's nothing we can
5318 do with that, so skip it. */
5321 if (err->apr_err == SVN_ERR_WC_NOT_LOCKED
5322 || err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5324 svn_error_clear(err);
5329 return svn_error_trace(err);
5333 /* If we are attempting to set empty revision range override mergeinfo
5334 on a path with no explicit mergeinfo, we first need the
5335 mergeinfo that path inherits. */
5336 if (mergeinfo == NULL && ranges->nelts == 0)
5338 SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, NULL,
5339 svn_mergeinfo_nearest_ancestor,
5340 local_abspath, NULL, NULL,
5341 FALSE, ctx, iterpool, iterpool));
5344 if (mergeinfo == NULL)
5345 mergeinfo = apr_hash_make(iterpool);
5347 local_abspath_rel_to_target = svn_dirent_skip_ancestor(target_abspath,
5349 SVN_ERR_ASSERT(local_abspath_rel_to_target != NULL);
5350 fspath = svn_fspath__join(source_fspath,
5351 local_abspath_rel_to_target,
5353 rangelist = svn_hash_gets(mergeinfo, fspath);
5354 if (rangelist == NULL)
5355 rangelist = apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
5359 ranges = svn_rangelist_dup(ranges, iterpool);
5360 SVN_ERR(svn_rangelist_reverse(ranges, iterpool));
5361 SVN_ERR(svn_rangelist_remove(&rangelist, ranges, rangelist,
5367 SVN_ERR(svn_rangelist_merge2(rangelist, ranges, iterpool, iterpool));
5369 /* Update the mergeinfo by adjusting the path's rangelist. */
5370 svn_hash_sets(mergeinfo, fspath, rangelist);
5372 if (is_rollback && apr_hash_count(mergeinfo) == 0)
5375 svn_mergeinfo__remove_empty_rangelists(mergeinfo, scratch_pool);
5379 svn_mergeinfo_t existing_mergeinfo =
5380 svn_hash_gets(result_catalog, local_abspath);
5381 apr_pool_t *result_catalog_pool = apr_hash_pool_get(result_catalog);
5383 if (existing_mergeinfo)
5384 SVN_ERR(svn_mergeinfo_merge2(mergeinfo, existing_mergeinfo,
5385 result_catalog_pool, scratch_pool));
5386 svn_hash_sets(result_catalog,
5387 apr_pstrdup(result_catalog_pool, local_abspath),
5388 svn_mergeinfo_dup(mergeinfo, result_catalog_pool));
5392 err = svn_client__record_wc_mergeinfo(local_abspath, mergeinfo,
5393 TRUE, ctx, iterpool);
5395 if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
5397 /* PATH isn't just missing, it's not even versioned as far
5398 as this working copy knows. But it was included in
5399 MERGES, which means that the server knows about it.
5400 Likely we don't have access to the source due to authz
5401 restrictions. For now just clear the error and
5404 ### TODO: Set non-inheritable mergeinfo on PATH's immediate
5405 ### parent and normal mergeinfo on PATH's siblings which we
5406 ### do have access to. */
5407 svn_error_clear(err);
5414 svn_pool_destroy(iterpool);
5415 return SVN_NO_ERROR;
5418 /* Helper for record_mergeinfo_for_dir_merge().
5420 Record override mergeinfo on any paths skipped during a merge.
5422 Set empty mergeinfo on each path in MERGE_B->SKIPPED_ABSPATHS so the path
5423 does not incorrectly inherit mergeinfo that will later be describing
5426 MERGEINFO_PATH and MERGE_B are cascaded from
5427 arguments of the same name in the caller.
5429 IS_ROLLBACK is true if the caller is recording a reverse merge and false
5430 otherwise. RANGELIST is the set of revisions being merged from
5431 MERGEINFO_PATH to MERGE_B->target. */
5432 static svn_error_t *
5433 record_skips_in_mergeinfo(const char *mergeinfo_path,
5434 const svn_rangelist_t *rangelist,
5435 svn_boolean_t is_rollback,
5436 merge_cmd_baton_t *merge_b,
5437 apr_pool_t *scratch_pool)
5439 apr_hash_index_t *hi;
5441 apr_size_t nbr_skips = apr_hash_count(merge_b->skipped_abspaths);
5442 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5445 return SVN_NO_ERROR;
5447 merges = apr_hash_make(scratch_pool);
5449 /* Override the mergeinfo for child paths which weren't actually merged. */
5450 for (hi = apr_hash_first(scratch_pool, merge_b->skipped_abspaths); hi;
5451 hi = apr_hash_next(hi))
5453 const char *skipped_abspath = apr_hash_this_key(hi);
5454 svn_wc_notify_state_t obstruction_state;
5456 svn_pool_clear(iterpool);
5458 /* Before we override, make sure this is a versioned path, it might
5459 be an external or missing from disk due to authz restrictions. */
5460 SVN_ERR(perform_obstruction_check(&obstruction_state, NULL, NULL,
5462 merge_b, skipped_abspath,
5464 if (obstruction_state == svn_wc_notify_state_obstructed
5465 || obstruction_state == svn_wc_notify_state_missing)
5468 /* Add an empty range list for this path.
5470 ### TODO: This works fine for a file path skipped because it is
5471 ### missing as long as the file's parent directory is present.
5472 ### But missing directory paths skipped are not handled yet,
5473 ### see issue #2915.
5475 ### TODO: An empty range is fine if the skipped path doesn't
5476 ### inherit any mergeinfo from a parent, but if it does
5477 ### we need to account for that. See issue #3440
5478 ### http://subversion.tigris.org/issues/show_bug.cgi?id=3440. */
5479 svn_hash_sets(merges, skipped_abspath,
5480 apr_array_make(scratch_pool, 0,
5481 sizeof(svn_merge_range_t *)));
5483 /* if (nbr_skips < notify_b->nbr_notifications)
5484 ### Use RANGELIST as the mergeinfo for all children of
5485 ### this path which were not also explicitly
5488 SVN_ERR(update_wc_mergeinfo(NULL, merge_b->target->abspath,
5489 mergeinfo_path, merges,
5490 is_rollback, merge_b->ctx, iterpool));
5491 svn_pool_destroy(iterpool);
5492 return SVN_NO_ERROR;
5495 /* Data for reporting when a merge aborted because of raising conflicts.
5497 typedef struct single_range_conflict_report_t
5499 /* What sub-range of the requested source raised conflicts?
5500 * The 'inheritable' flag is ignored. */
5501 merge_source_t *conflicted_range;
5502 /* What sub-range of the requested source remains to be merged?
5503 * NULL if no more. The 'inheritable' flag is ignored. */
5504 merge_source_t *remaining_source;
5506 } single_range_conflict_report_t;
5508 /* Create a single_range_conflict_report_t, containing deep copies of
5509 * CONFLICTED_RANGE and REMAINING_SOURCE, allocated in RESULT_POOL. */
5510 static single_range_conflict_report_t *
5511 single_range_conflict_report_create(const merge_source_t *conflicted_range,
5512 const merge_source_t *remaining_source,
5513 apr_pool_t *result_pool)
5515 single_range_conflict_report_t *report
5516 = apr_palloc(result_pool, sizeof(*report));
5518 assert(conflicted_range != NULL);
5520 report->conflicted_range = merge_source_dup(conflicted_range, result_pool);
5521 report->remaining_source
5522 = remaining_source ? merge_source_dup(remaining_source, result_pool)
5527 /* Data for reporting when a merge aborted because of raising conflicts.
5529 * ### TODO: More info, including the ranges (or other parameters) the user
5530 * needs to complete the merge.
5532 typedef struct conflict_report_t
5534 const char *target_abspath;
5535 /* The revision range during which conflicts were raised */
5536 const merge_source_t *conflicted_range;
5537 /* Was the conflicted range the last range in the whole requested merge? */
5538 svn_boolean_t was_last_range;
5539 } conflict_report_t;
5541 /* Return a new conflict_report_t containing deep copies of the parameters,
5542 * allocated in RESULT_POOL. */
5543 static conflict_report_t *
5544 conflict_report_create(const char *target_abspath,
5545 const merge_source_t *conflicted_range,
5546 svn_boolean_t was_last_range,
5547 apr_pool_t *result_pool)
5549 conflict_report_t *report = apr_palloc(result_pool, sizeof(*report));
5551 report->target_abspath = apr_pstrdup(result_pool, target_abspath);
5552 report->conflicted_range = merge_source_dup(conflicted_range, result_pool);
5553 report->was_last_range = was_last_range;
5557 /* Return a deep copy of REPORT, allocated in RESULT_POOL. */
5558 static conflict_report_t *
5559 conflict_report_dup(const conflict_report_t *report,
5560 apr_pool_t *result_pool)
5562 conflict_report_t *new = apr_pmemdup(result_pool, report, sizeof(*new));
5564 new->target_abspath = apr_pstrdup(result_pool, report->target_abspath);
5565 new->conflicted_range = merge_source_dup(report->conflicted_range,
5570 /* Create and return an error structure appropriate for the unmerged
5571 revisions range(s). */
5572 static APR_INLINE svn_error_t *
5573 make_merge_conflict_error(conflict_report_t *report,
5574 apr_pool_t *scratch_pool)
5576 assert(!report || svn_dirent_is_absolute(report->target_abspath));
5578 if (report && ! report->was_last_range)
5580 svn_error_t *err = svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
5581 _("One or more conflicts were produced while merging r%ld:%ld into\n"
5583 "resolve all conflicts and rerun the merge to apply the remaining\n"
5584 "unmerged revisions"),
5585 report->conflicted_range->loc1->rev, report->conflicted_range->loc2->rev,
5586 svn_dirent_local_style(report->target_abspath, scratch_pool));
5587 assert(report->conflicted_range->loc1->rev != report->conflicted_range->loc2->rev); /* ### is a valid case in a 2-URL merge */
5590 return SVN_NO_ERROR;
5593 /* Helper for do_directory_merge().
5595 TARGET_WCPATH is a directory and CHILDREN_WITH_MERGEINFO is filled
5596 with paths (svn_client__merge_path_t *) arranged in depth first order,
5597 which have mergeinfo set on them or meet one of the other criteria
5598 defined in get_mergeinfo_paths(). Remove any paths absent from disk
5599 or scheduled for deletion from CHILDREN_WITH_MERGEINFO which are equal to
5600 or are descendants of TARGET_WCPATH by setting those children to NULL. */
5602 remove_absent_children(const char *target_wcpath,
5603 apr_array_header_t *children_with_mergeinfo)
5605 /* Before we try to override mergeinfo for skipped paths, make sure
5606 the path isn't absent due to authz restrictions, because there's
5607 nothing we can do about those. */
5609 for (i = 0; i < children_with_mergeinfo->nelts; i++)
5611 svn_client__merge_path_t *child =
5612 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5613 if ((child->absent || child->scheduled_for_deletion)
5614 && svn_dirent_is_ancestor(target_wcpath, child->abspath))
5616 svn_sort__array_delete(children_with_mergeinfo, i--, 1);
5621 /* Helper for do_directory_merge() to handle the case where a merge editor
5622 drive removes explicit mergeinfo from a subtree of the merge target.
5624 MERGE_B is cascaded from the argument of the same name in
5625 do_directory_merge(). For each path (if any) in
5626 MERGE_B->PATHS_WITH_DELETED_MERGEINFO remove that path from
5627 CHILDREN_WITH_MERGEINFO.
5629 The one exception is for the merge target itself,
5630 MERGE_B->target->abspath, this must always be present in
5631 CHILDREN_WITH_MERGEINFO so this is never removed by this
5634 remove_children_with_deleted_mergeinfo(merge_cmd_baton_t *merge_b,
5635 apr_array_header_t *children_with_mergeinfo)
5639 if (!merge_b->paths_with_deleted_mergeinfo)
5642 /* CHILDREN_WITH_MERGEINFO[0] is the always the merge target
5643 so start at the first child. */
5644 for (i = 1; i < children_with_mergeinfo->nelts; i++)
5646 svn_client__merge_path_t *child =
5647 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5649 if (svn_hash_gets(merge_b->paths_with_deleted_mergeinfo, child->abspath))
5651 svn_sort__array_delete(children_with_mergeinfo, i--, 1);
5656 /* Helper for do_directory_merge().
5658 Set up the diff editor report to merge the SOURCE diff
5659 into TARGET_ABSPATH and drive it.
5661 If mergeinfo is not being honored (based on MERGE_B -- see the doc
5662 string for HONOR_MERGEINFO() for how this is determined), then ignore
5663 CHILDREN_WITH_MERGEINFO and merge the SOURCE diff to TARGET_ABSPATH.
5665 If mergeinfo is being honored then perform a history-aware merge,
5666 describing TARGET_ABSPATH and its subtrees to the reporter in such as way
5667 as to avoid repeating merges already performed per the mergeinfo and
5668 natural history of TARGET_ABSPATH and its subtrees.
5670 The ranges that still need to be merged to the TARGET_ABSPATH and its
5671 subtrees are described in CHILDREN_WITH_MERGEINFO, an array of
5672 svn_client__merge_path_t * -- see 'THE CHILDREN_WITH_MERGEINFO ARRAY'
5673 comment at the top of this file for more info. Note that it is possible
5674 TARGET_ABSPATH and/or some of its subtrees need only a subset, or no part,
5675 of SOURCE to be merged. Though there is little point to
5676 calling this function if TARGET_ABSPATH and all its subtrees have already
5677 had SOURCE merged, this will work but is a no-op.
5679 SOURCE->rev1 and SOURCE->rev2 must be bound by the set of remaining_ranges
5680 fields in CHILDREN_WITH_MERGEINFO's elements, specifically:
5682 For forward merges (SOURCE->rev1 < SOURCE->rev2):
5684 1) The first svn_merge_range_t * element of each child's remaining_ranges
5685 array must meet one of the following conditions:
5687 a) The range's start field is greater than or equal to SOURCE->rev2.
5689 b) The range's end field is SOURCE->rev2.
5691 2) Among all the ranges that meet condition 'b' the oldest start
5692 revision must equal SOURCE->rev1.
5694 For reverse merges (SOURCE->rev1 > SOURCE->rev2):
5696 1) The first svn_merge_range_t * element of each child's remaining_ranges
5697 array must meet one of the following conditions:
5699 a) The range's start field is less than or equal to SOURCE->rev2.
5701 b) The range's end field is SOURCE->rev2.
5703 2) Among all the ranges that meet condition 'b' the youngest start
5704 revision must equal SOURCE->rev1.
5706 Note: If the first svn_merge_range_t * element of some subtree child's
5707 remaining_ranges array is the same as the first range of that child's
5708 nearest path-wise ancestor, then the subtree child *will not* be described
5711 DEPTH, NOTIFY_B, and MERGE_B are cascaded from do_directory_merge(), see
5712 that function for more info.
5714 MERGE_B->ra_session1 and MERGE_B->ra_session2 are RA sessions open to any
5715 URL in the repository of SOURCE; they may be temporarily reparented within
5718 If SOURCE->ancestral is set, then SOURCE->loc1 must be a
5719 historical ancestor of SOURCE->loc2, or vice-versa (see
5720 `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements around
5721 SOURCE in this case).
5723 static svn_error_t *
5724 drive_merge_report_editor(const char *target_abspath,
5725 const merge_source_t *source,
5726 const apr_array_header_t *children_with_mergeinfo,
5727 const svn_diff_tree_processor_t *processor,
5729 merge_cmd_baton_t *merge_b,
5730 apr_pool_t *scratch_pool)
5732 const svn_ra_reporter3_t *reporter;
5733 const svn_delta_editor_t *diff_editor;
5734 void *diff_edit_baton;
5736 svn_revnum_t target_start;
5737 svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
5738 const char *old_sess1_url, *old_sess2_url;
5739 svn_boolean_t is_rollback = source->loc1->rev > source->loc2->rev;
5741 /* Start with a safe default starting revision for the editor and the
5743 target_start = source->loc1->rev;
5745 /* If we are honoring mergeinfo the starting revision for the merge target
5746 might not be SOURCE->rev1, in fact the merge target might not need *any*
5747 part of SOURCE merged -- Instead some subtree of the target
5748 needs SOURCE -- So get the right starting revision for the
5750 if (honor_mergeinfo)
5752 svn_client__merge_path_t *child;
5754 /* CHILDREN_WITH_MERGEINFO must always exist if we are honoring
5755 mergeinfo and must have at least one element (describing the
5757 SVN_ERR_ASSERT(children_with_mergeinfo);
5758 SVN_ERR_ASSERT(children_with_mergeinfo->nelts);
5760 /* Get the merge target's svn_client__merge_path_t, which is always
5761 the first in the array due to depth first sorting requirement,
5762 see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
5763 child = APR_ARRAY_IDX(children_with_mergeinfo, 0,
5764 svn_client__merge_path_t *);
5765 SVN_ERR_ASSERT(child);
5766 if (child->remaining_ranges->nelts == 0)
5768 /* The merge target doesn't need anything merged. */
5769 target_start = source->loc2->rev;
5773 /* The merge target has remaining revisions to merge. These
5774 ranges may fully or partially overlap the range described
5775 by SOURCE->rev1:rev2 or may not intersect that range at
5777 svn_merge_range_t *range =
5778 APR_ARRAY_IDX(child->remaining_ranges, 0,
5779 svn_merge_range_t *);
5780 if ((!is_rollback && range->start > source->loc2->rev)
5781 || (is_rollback && range->start < source->loc2->rev))
5783 /* Merge target's first remaining range doesn't intersect. */
5784 target_start = source->loc2->rev;
5788 /* Merge target's first remaining range partially or
5790 target_start = range->start;
5795 SVN_ERR(svn_client__ensure_ra_session_url(&old_sess1_url,
5796 merge_b->ra_session1,
5797 source->loc1->url, scratch_pool));
5798 /* Temporarily point our second RA session to SOURCE->loc1->url, too. We use
5799 this to request individual file contents. */
5800 SVN_ERR(svn_client__ensure_ra_session_url(&old_sess2_url,
5801 merge_b->ra_session2,
5802 source->loc1->url, scratch_pool));
5804 /* Get the diff editor and a reporter with which to, ultimately,
5806 SVN_ERR(svn_client__get_diff_editor2(&diff_editor, &diff_edit_baton,
5807 merge_b->ra_session2,
5810 TRUE /* text_deltas */,
5812 merge_b->ctx->cancel_func,
5813 merge_b->ctx->cancel_baton,
5815 SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1,
5816 &reporter, &report_baton, source->loc2->rev,
5817 "", depth, merge_b->diff_ignore_ancestry,
5818 TRUE, /* text_deltas */
5819 source->loc2->url, diff_editor, diff_edit_baton,
5822 /* Drive the reporter. */
5823 SVN_ERR(reporter->set_path(report_baton, "", target_start, depth,
5824 FALSE, NULL, scratch_pool));
5825 if (honor_mergeinfo && children_with_mergeinfo)
5827 /* Describe children with mergeinfo overlapping this merge
5828 operation such that no repeated diff is retrieved for them from
5831 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5833 /* Start with CHILDREN_WITH_MERGEINFO[1], CHILDREN_WITH_MERGEINFO[0]
5834 is always the merge target (TARGET_ABSPATH). */
5835 for (i = 1; i < children_with_mergeinfo->nelts; i++)
5837 svn_merge_range_t *range;
5838 const char *child_repos_path;
5839 const svn_client__merge_path_t *parent;
5840 const svn_client__merge_path_t *child =
5841 APR_ARRAY_IDX(children_with_mergeinfo, i,
5842 svn_client__merge_path_t *);
5844 SVN_ERR_ASSERT(child);
5848 svn_pool_clear(iterpool);
5850 /* Find this child's nearest wc ancestor with mergeinfo. */
5851 parent = find_nearest_ancestor(children_with_mergeinfo,
5852 FALSE, child->abspath);
5854 /* If a subtree needs the same range applied as its nearest parent
5855 with mergeinfo or neither the subtree nor this parent need
5856 SOURCE->rev1:rev2 merged, then we don't need to describe the
5857 subtree separately. In the latter case this could break the
5858 editor if child->abspath didn't exist at SOURCE->rev2 and we
5859 attempt to describe it via a reporter set_path call. */
5860 if (child->remaining_ranges->nelts)
5862 range = APR_ARRAY_IDX(child->remaining_ranges, 0,
5863 svn_merge_range_t *);
5864 if ((!is_rollback && range->start > source->loc2->rev)
5865 || (is_rollback && range->start < source->loc2->rev))
5867 /* This child's first remaining range comes after the range
5868 we are currently merging, so skip it. We expect to get
5869 to it in a subsequent call to this function. */
5872 else if (parent->remaining_ranges->nelts)
5874 svn_merge_range_t *parent_range =
5875 APR_ARRAY_IDX(parent->remaining_ranges, 0,
5876 svn_merge_range_t *);
5877 svn_merge_range_t *child_range =
5878 APR_ARRAY_IDX(child->remaining_ranges, 0,
5879 svn_merge_range_t *);
5880 if (parent_range->start == child_range->start)
5881 continue; /* Subtree needs same range as parent. */
5884 else /* child->remaining_ranges->nelts == 0*/
5886 /* If both the subtree and its parent need no ranges applied
5887 consider that as the "same ranges" and don't describe
5889 if (parent->remaining_ranges->nelts == 0)
5893 /* Ok, we really need to describe this subtree as it needs different
5894 ranges applied than its nearest working copy parent. */
5895 child_repos_path = svn_dirent_is_child(target_abspath,
5898 /* This loop is only processing subtrees, so CHILD->ABSPATH
5899 better be a proper child of the merge target. */
5900 SVN_ERR_ASSERT(child_repos_path);
5902 if ((child->remaining_ranges->nelts == 0)
5903 || (is_rollback && (range->start < source->loc2->rev))
5904 || (!is_rollback && (range->start > source->loc2->rev)))
5906 /* Nothing to merge to this child. We'll claim we have
5907 it up to date so the server doesn't send us
5909 SVN_ERR(reporter->set_path(report_baton, child_repos_path,
5910 source->loc2->rev, depth, FALSE,
5915 SVN_ERR(reporter->set_path(report_baton, child_repos_path,
5916 range->start, depth, FALSE,
5920 svn_pool_destroy(iterpool);
5922 SVN_ERR(reporter->finish_report(report_baton, scratch_pool));
5924 /* Point the merge baton's RA sessions back where they were. */
5925 SVN_ERR(svn_ra_reparent(merge_b->ra_session1, old_sess1_url, scratch_pool));
5926 SVN_ERR(svn_ra_reparent(merge_b->ra_session2, old_sess2_url, scratch_pool));
5928 return SVN_NO_ERROR;
5931 /* Iterate over each svn_client__merge_path_t * element in
5932 CHILDREN_WITH_MERGEINFO and, if START_REV is true, find the most inclusive
5933 start revision among those element's first remaining_ranges element. If
5934 START_REV is false, then look for the most inclusive end revision.
5936 If IS_ROLLBACK is true the youngest start or end (as per START_REV)
5937 revision is considered the "most inclusive" otherwise the oldest revision
5940 If none of CHILDREN_WITH_MERGEINFO's elements have any remaining ranges
5941 return SVN_INVALID_REVNUM. */
5943 get_most_inclusive_rev(const apr_array_header_t *children_with_mergeinfo,
5944 svn_boolean_t is_rollback,
5945 svn_boolean_t start_rev)
5948 svn_revnum_t most_inclusive_rev = SVN_INVALID_REVNUM;
5950 for (i = 0; i < children_with_mergeinfo->nelts; i++)
5952 svn_client__merge_path_t *child =
5953 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5955 if ((! child) || child->absent)
5957 if (child->remaining_ranges->nelts > 0)
5959 svn_merge_range_t *range =
5960 APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
5962 /* Are we looking for the most inclusive start or end rev? */
5963 svn_revnum_t rev = start_rev ? range->start : range->end;
5965 if ((most_inclusive_rev == SVN_INVALID_REVNUM)
5966 || (is_rollback && (rev > most_inclusive_rev))
5967 || ((! is_rollback) && (rev < most_inclusive_rev)))
5968 most_inclusive_rev = rev;
5971 return most_inclusive_rev;
5975 /* If first item in each child of CHILDREN_WITH_MERGEINFO's
5976 remaining_ranges is inclusive of END_REV, Slice the first range in
5977 to two at END_REV. All the allocations are persistent and allocated
5980 slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
5981 svn_boolean_t is_rollback, svn_revnum_t end_rev,
5985 for (i = 0; i < children_with_mergeinfo->nelts; i++)
5987 svn_client__merge_path_t *child =
5988 APR_ARRAY_IDX(children_with_mergeinfo, i,
5989 svn_client__merge_path_t *);
5990 if (!child || child->absent)
5992 if (child->remaining_ranges->nelts > 0)
5994 svn_merge_range_t *range = APR_ARRAY_IDX(child->remaining_ranges, 0,
5995 svn_merge_range_t *);
5996 if ((is_rollback && (range->start > end_rev)
5997 && (range->end < end_rev))
5998 || (!is_rollback && (range->start < end_rev)
5999 && (range->end > end_rev)))
6001 svn_merge_range_t *split_range1, *split_range2;
6003 split_range1 = svn_merge_range_dup(range, pool);
6004 split_range2 = svn_merge_range_dup(range, pool);
6005 split_range1->end = end_rev;
6006 split_range2->start = end_rev;
6007 APR_ARRAY_IDX(child->remaining_ranges, 0,
6008 svn_merge_range_t *) = split_range1;
6009 svn_sort__array_insert(child->remaining_ranges, &split_range2, 1);
6015 /* Helper for do_directory_merge().
6017 For each child in CHILDREN_WITH_MERGEINFO remove the first remaining_ranges
6018 svn_merge_range_t *element of the child if that range has an end revision
6021 If a range is removed from a child's remaining_ranges array, allocate the
6022 new remaining_ranges array in POOL.
6025 remove_first_range_from_remaining_ranges(svn_revnum_t revision,
6027 *children_with_mergeinfo,
6032 for (i = 0; i < children_with_mergeinfo->nelts; i++)
6034 svn_client__merge_path_t *child =
6035 APR_ARRAY_IDX(children_with_mergeinfo, i,
6036 svn_client__merge_path_t *);
6037 if (!child || child->absent)
6039 if (child->remaining_ranges->nelts > 0)
6041 svn_merge_range_t *first_range =
6042 APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
6043 if (first_range->end == revision)
6045 svn_sort__array_delete(child->remaining_ranges, 0, 1);
6051 /* Get a file's content and properties from the repository.
6052 Set *FILENAME to the local path to a new temporary file holding its text,
6053 and set *PROPS to a new hash of its properties.
6055 RA_SESSION is a session open to the correct repository, which will be
6056 temporarily reparented to the URL of the file itself. LOCATION is the
6057 repository location of the file.
6059 The resulting file and the return values live as long as RESULT_POOL, all
6060 other allocations occur in SCRATCH_POOL.
6062 static svn_error_t *
6063 single_file_merge_get_file(const char **filename,
6065 svn_ra_session_t *ra_session,
6066 const svn_client__pathrev_t *location,
6067 const char *wc_target,
6068 apr_pool_t *result_pool,
6069 apr_pool_t *scratch_pool)
6071 svn_stream_t *stream;
6072 const char *old_sess_url;
6075 SVN_ERR(svn_stream_open_unique(&stream, filename, NULL,
6076 svn_io_file_del_on_pool_cleanup,
6077 result_pool, scratch_pool));
6079 SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url, ra_session, location->url,
6081 err = svn_ra_get_file(ra_session, "", location->rev,
6082 stream, NULL, props, scratch_pool);
6083 SVN_ERR(svn_error_compose_create(
6084 err, svn_ra_reparent(ra_session, old_sess_url, scratch_pool)));
6086 return svn_error_trace(svn_stream_close(stream));
6089 /* Compare two svn_client__merge_path_t elements **A and **B, given the
6090 addresses of pointers to them. Return an integer less than, equal to, or
6091 greater than zero if A sorts before, the same as, or after B, respectively.
6092 This is a helper for qsort() and bsearch() on an array of such elements. */
6094 compare_merge_path_t_as_paths(const void *a,
6097 const svn_client__merge_path_t *child1
6098 = *((const svn_client__merge_path_t * const *) a);
6099 const svn_client__merge_path_t *child2
6100 = *((const svn_client__merge_path_t * const *) b);
6102 return svn_path_compare_paths(child1->abspath, child2->abspath);
6105 /* Return a pointer to the element of CHILDREN_WITH_MERGEINFO whose path
6106 * is PATH, or return NULL if there is no such element. */
6107 static svn_client__merge_path_t *
6108 get_child_with_mergeinfo(const apr_array_header_t *children_with_mergeinfo,
6109 const char *abspath)
6111 svn_client__merge_path_t merge_path;
6112 svn_client__merge_path_t *key;
6113 svn_client__merge_path_t **pchild;
6115 merge_path.abspath = abspath;
6117 pchild = bsearch(&key, children_with_mergeinfo->elts,
6118 children_with_mergeinfo->nelts,
6119 children_with_mergeinfo->elt_size,
6120 compare_merge_path_t_as_paths);
6121 return pchild ? *pchild : NULL;
6124 /* Insert a deep copy of INSERT_ELEMENT into the CHILDREN_WITH_MERGEINFO
6125 array at its correct position. Allocate the new storage in POOL.
6126 CHILDREN_WITH_MERGEINFO is a depth first sorted array of
6127 (svn_client__merge_path_t *).
6129 ### Most callers don't need this to deep-copy the new element.
6130 ### It may be more efficient for some callers to insert a bunch of items
6131 out of order and then sort afterwards. (One caller is doing a qsort
6132 after calling this anyway.)
6135 insert_child_to_merge(apr_array_header_t *children_with_mergeinfo,
6136 const svn_client__merge_path_t *insert_element,
6140 const svn_client__merge_path_t *new_element;
6142 /* Find where to insert the new element */
6144 svn_sort__bsearch_lower_bound(children_with_mergeinfo, &insert_element,
6145 compare_merge_path_t_as_paths);
6147 new_element = svn_client__merge_path_dup(insert_element, pool);
6148 svn_sort__array_insert(children_with_mergeinfo, &new_element, insert_index);
6151 /* Helper for get_mergeinfo_paths().
6153 CHILDREN_WITH_MERGEINFO, DEPTH, and POOL are
6154 all cascaded from the arguments of the same name to get_mergeinfo_paths().
6156 TARGET is the merge target.
6158 *CHILD is the element in in CHILDREN_WITH_MERGEINFO that
6159 get_mergeinfo_paths() is iterating over and *CURR_INDEX is index for
6162 If CHILD->ABSPATH is equal to MERGE_CMD_BATON->target->abspath do nothing.
6163 Else if CHILD->ABSPATH is switched or absent then make sure its immediate
6164 (as opposed to nearest) parent in CHILDREN_WITH_MERGEINFO is marked as
6165 missing a child. If the immediate parent does not exist in
6166 CHILDREN_WITH_MERGEINFO then create it (and increment *CURR_INDEX so that
6167 caller doesn't process the inserted element). Also ensure that
6168 CHILD->ABSPATH's siblings which are not already present in
6169 CHILDREN_WITH_MERGEINFO are also added to the array, limited by DEPTH
6170 (e.g. don't add directory siblings of a switched file).
6171 Use POOL for temporary allocations only, any new CHILDREN_WITH_MERGEINFO
6172 elements are allocated in POOL. */
6173 static svn_error_t *
6174 insert_parent_and_sibs_of_sw_absent_del_subtree(
6175 apr_array_header_t *children_with_mergeinfo,
6176 const merge_target_t *target,
6178 svn_client__merge_path_t *child,
6180 svn_client_ctx_t *ctx,
6183 svn_client__merge_path_t *parent;
6184 const char *parent_abspath;
6185 apr_pool_t *iterpool;
6186 const apr_array_header_t *children;
6191 && strcmp(target->abspath,
6192 child->abspath) != 0)))
6193 return SVN_NO_ERROR;
6195 parent_abspath = svn_dirent_dirname(child->abspath, pool);
6196 parent = get_child_with_mergeinfo(children_with_mergeinfo, parent_abspath);
6199 parent->missing_child = child->absent;
6200 parent->switched_child = child->switched;
6204 /* Create a new element to insert into CHILDREN_WITH_MERGEINFO. */
6205 parent = svn_client__merge_path_create(parent_abspath, pool);
6206 parent->missing_child = child->absent;
6207 parent->switched_child = child->switched;
6208 /* Insert PARENT into CHILDREN_WITH_MERGEINFO. */
6209 insert_child_to_merge(children_with_mergeinfo, parent, pool);
6210 /* Increment for loop index so we don't process the inserted element. */
6212 } /*(parent == NULL) */
6214 /* Add all of PARENT's non-missing children that are not already present.*/
6215 SVN_ERR(svn_wc__node_get_children_of_working_node(&children, ctx->wc_ctx,
6218 iterpool = svn_pool_create(pool);
6219 for (i = 0; i < children->nelts; i++)
6221 const char *child_abspath = APR_ARRAY_IDX(children, i, const char *);
6222 svn_client__merge_path_t *sibling_of_missing;
6224 svn_pool_clear(iterpool);
6226 /* Does this child already exist in CHILDREN_WITH_MERGEINFO? */
6227 sibling_of_missing = get_child_with_mergeinfo(children_with_mergeinfo,
6229 /* Create the missing child and insert it into CHILDREN_WITH_MERGEINFO.*/
6230 if (!sibling_of_missing)
6232 /* Don't add directory children if DEPTH is svn_depth_files. */
6233 if (depth == svn_depth_files)
6235 svn_node_kind_t child_kind;
6237 SVN_ERR(svn_wc_read_kind2(&child_kind,
6238 ctx->wc_ctx, child_abspath,
6239 FALSE, FALSE, iterpool));
6240 if (child_kind != svn_node_file)
6244 sibling_of_missing = svn_client__merge_path_create(child_abspath,
6246 insert_child_to_merge(children_with_mergeinfo, sibling_of_missing,
6251 svn_pool_destroy(iterpool);
6253 return SVN_NO_ERROR;
6256 /* pre_merge_status_cb's baton */
6257 struct pre_merge_status_baton_t
6259 svn_wc_context_t *wc_ctx;
6261 /* const char *absolute_wc_path to svn_depth_t * mapping for depths
6262 of empty, immediates, and files. */
6263 apr_hash_t *shallow_subtrees;
6265 /* const char *absolute_wc_path to the same, for all paths missing
6266 from the working copy. */
6267 apr_hash_t *missing_subtrees;
6269 /* const char *absolute_wc_path const char * repos relative path, describing
6270 the root of each switched subtree in the working copy and the repository
6271 relative path it is switched to. */
6272 apr_hash_t *switched_subtrees;
6274 /* A pool to allocate additions to the above hashes in. */
6278 /* A svn_wc_status_func4_t callback used by get_mergeinfo_paths to gather
6279 all switched, depth filtered and missing subtrees under a merge target.
6281 Note that this doesn't see server and user excluded trees. */
6282 static svn_error_t *
6283 pre_merge_status_cb(void *baton,
6284 const char *local_abspath,
6285 const svn_wc_status3_t *status,
6286 apr_pool_t *scratch_pool)
6288 struct pre_merge_status_baton_t *pmsb = baton;
6290 if (status->switched && !status->file_external)
6292 store_path(pmsb->switched_subtrees, local_abspath);
6295 if (status->depth == svn_depth_empty
6296 || status->depth == svn_depth_files)
6298 const char *dup_abspath;
6299 svn_depth_t *depth = apr_pmemdup(pmsb->pool, &status->depth,
6302 dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
6304 svn_hash_sets(pmsb->shallow_subtrees, dup_abspath, depth);
6307 if (status->node_status == svn_wc_status_missing)
6309 svn_boolean_t new_missing_root = TRUE;
6310 apr_hash_index_t *hi;
6312 for (hi = apr_hash_first(scratch_pool, pmsb->missing_subtrees);
6314 hi = apr_hash_next(hi))
6316 const char *missing_root_path = apr_hash_this_key(hi);
6318 if (svn_dirent_is_ancestor(missing_root_path,
6321 new_missing_root = FALSE;
6326 if (new_missing_root)
6327 store_path(pmsb->missing_subtrees, local_abspath);
6330 return SVN_NO_ERROR;
6333 /* Find all the subtrees in the working copy tree rooted at TARGET_ABSPATH
6334 * that have explicit mergeinfo.
6335 * Set *SUBTREES_WITH_MERGEINFO to a hash mapping (const char *) absolute
6336 * WC path to (svn_mergeinfo_t *) mergeinfo.
6338 * ### Is this function equivalent to:
6340 * svn_client__get_wc_mergeinfo_catalog(
6341 * subtrees_with_mergeinfo, inherited=NULL, include_descendants=TRUE,
6342 * svn_mergeinfo_explicit, target_abspath, limit_path=NULL,
6343 * walked_path=NULL, ignore_invalid_mergeinfo=FALSE, ...)
6345 * except for the catalog keys being abspaths instead of repo-relpaths?
6347 static svn_error_t *
6348 get_wc_explicit_mergeinfo_catalog(apr_hash_t **subtrees_with_mergeinfo,
6349 const char *target_abspath,
6351 svn_client_ctx_t *ctx,
6352 apr_pool_t *result_pool,
6353 apr_pool_t *scratch_pool)
6355 svn_opt_revision_t working_revision = { svn_opt_revision_working, { 0 } };
6356 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6357 apr_hash_index_t *hi;
6358 apr_hash_t *externals;
6360 SVN_ERR(svn_client_propget5(subtrees_with_mergeinfo, NULL,
6361 SVN_PROP_MERGEINFO, target_abspath,
6362 &working_revision, &working_revision, NULL,
6363 depth, NULL, ctx, result_pool, scratch_pool));
6365 SVN_ERR(svn_wc__externals_defined_below(&externals, ctx->wc_ctx,
6366 target_abspath, scratch_pool,
6369 /* Convert property values to svn_mergeinfo_t. */
6370 for (hi = apr_hash_first(scratch_pool, *subtrees_with_mergeinfo);
6372 hi = apr_hash_next(hi))
6374 const char *wc_path = apr_hash_this_key(hi);
6375 svn_string_t *mergeinfo_string = apr_hash_this_val(hi);
6376 svn_mergeinfo_t mergeinfo;
6379 /* svn_client_propget5 picks up file externals with
6380 mergeinfo, but we don't want those. */
6381 if (svn_hash_gets(externals, wc_path))
6383 svn_hash_sets(*subtrees_with_mergeinfo, wc_path, NULL);
6387 svn_pool_clear(iterpool);
6389 err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_string->data,
6393 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
6395 err = svn_error_createf(
6396 SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
6397 _("Invalid mergeinfo detected on '%s', "
6398 "merge tracking not possible"),
6399 svn_dirent_local_style(wc_path, scratch_pool));
6401 return svn_error_trace(err);
6403 svn_hash_sets(*subtrees_with_mergeinfo, wc_path, mergeinfo);
6405 svn_pool_destroy(iterpool);
6407 return SVN_NO_ERROR;
6410 /* Helper for do_directory_merge() when performing merge-tracking aware
6413 Walk of the working copy tree rooted at TARGET->abspath to
6414 depth DEPTH. Create an svn_client__merge_path_t * for any path which meets
6415 one or more of the following criteria:
6417 1) Path has working svn:mergeinfo.
6418 2) Path is switched.
6419 3) Path is a subtree of the merge target (i.e. is not equal to
6420 TARGET->abspath) and has no mergeinfo of its own but
6421 its immediate parent has mergeinfo with non-inheritable ranges. If
6422 this isn't a dry-run and the merge is between differences in the same
6423 repository, then this function will set working mergeinfo on the path
6424 equal to the mergeinfo inheritable from its parent.
6425 4) Path has an immediate child (or children) missing from the WC because
6426 the child is switched or absent from the WC, or due to a sparse
6428 5) Path has a sibling (or siblings) missing from the WC because the
6429 sibling is switched, absent, scheduled for deletion, or missing due to
6431 6) Path is absent from disk due to an authz restriction.
6432 7) Path is equal to TARGET->abspath.
6433 8) Path is an immediate *directory* child of
6434 TARGET->abspath and DEPTH is svn_depth_immediates.
6435 9) Path is an immediate *file* child of TARGET->abspath
6436 and DEPTH is svn_depth_files.
6437 10) Path is at a depth of 'empty' or 'files'.
6438 11) Path is missing from disk (e.g. due to an OS-level deletion).
6440 If subtrees within the requested DEPTH are unexpectedly missing disk,
6441 then raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE.
6443 Store the svn_client__merge_path_t *'s in *CHILDREN_WITH_MERGEINFO in
6444 depth-first order based on the svn_client__merge_path_t *s path member as
6445 sorted by svn_path_compare_paths(). Set the remaining_ranges field of each
6448 Note: Since the walk is rooted at TARGET->abspath, the
6449 latter is guaranteed to be in *CHILDREN_WITH_MERGEINFO and due to the
6450 depth-first ordering it is guaranteed to be the first element in
6451 *CHILDREN_WITH_MERGEINFO.
6453 MERGE_CMD_BATON is cascaded from the argument of the same name in
6454 do_directory_merge().
6456 static svn_error_t *
6457 get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
6458 const merge_target_t *target,
6460 svn_boolean_t dry_run,
6461 svn_boolean_t same_repos,
6462 svn_client_ctx_t *ctx,
6463 apr_pool_t *result_pool,
6464 apr_pool_t *scratch_pool)
6467 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6468 apr_hash_t *subtrees_with_mergeinfo;
6469 apr_hash_t *excluded_subtrees;
6470 apr_hash_t *switched_subtrees;
6471 apr_hash_t *shallow_subtrees;
6472 apr_hash_t *missing_subtrees;
6473 struct pre_merge_status_baton_t pre_merge_status_baton;
6475 /* Case 1: Subtrees with explicit mergeinfo. */
6476 SVN_ERR(get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
6479 result_pool, scratch_pool));
6480 if (subtrees_with_mergeinfo)
6482 apr_hash_index_t *hi;
6484 for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
6486 hi = apr_hash_next(hi))
6488 const char *wc_path = apr_hash_this_key(hi);
6489 svn_mergeinfo_t mergeinfo = apr_hash_this_val(hi);
6490 svn_client__merge_path_t *mergeinfo_child =
6491 svn_client__merge_path_create(wc_path, result_pool);
6493 svn_pool_clear(iterpool);
6495 /* Stash this child's pre-existing mergeinfo. */
6496 mergeinfo_child->pre_merge_mergeinfo = mergeinfo;
6498 /* Note if this child has non-inheritable mergeinfo */
6499 mergeinfo_child->has_noninheritable
6500 = svn_mergeinfo__is_noninheritable(
6501 mergeinfo_child->pre_merge_mergeinfo, iterpool);
6503 /* Append it. We'll sort below. */
6504 APR_ARRAY_PUSH(children_with_mergeinfo, svn_client__merge_path_t *)
6505 = svn_client__merge_path_dup(mergeinfo_child, result_pool);
6508 /* Sort CHILDREN_WITH_MERGEINFO by each child's path (i.e. as per
6509 compare_merge_path_t_as_paths). Any subsequent insertions of new
6510 children with insert_child_to_merge() require this ordering. */
6511 qsort(children_with_mergeinfo->elts,
6512 children_with_mergeinfo->nelts,
6513 children_with_mergeinfo->elt_size,
6514 compare_merge_path_t_as_paths);
6517 /* Case 2: Switched subtrees
6518 Case 10: Paths at depths of 'empty' or 'files'
6519 Case 11: Paths missing from disk */
6520 pre_merge_status_baton.wc_ctx = ctx->wc_ctx;
6521 switched_subtrees = apr_hash_make(scratch_pool);
6522 pre_merge_status_baton.switched_subtrees = switched_subtrees;
6523 shallow_subtrees = apr_hash_make(scratch_pool);
6524 pre_merge_status_baton.shallow_subtrees = shallow_subtrees;
6525 missing_subtrees = apr_hash_make(scratch_pool);
6526 pre_merge_status_baton.missing_subtrees = missing_subtrees;
6527 pre_merge_status_baton.pool = scratch_pool;
6528 SVN_ERR(svn_wc_walk_status(ctx->wc_ctx,
6532 FALSE /* no_ignore */,
6533 TRUE /* ignore_text_mods */,
6534 NULL /* ingore_patterns */,
6535 pre_merge_status_cb, &pre_merge_status_baton,
6536 ctx->cancel_func, ctx->cancel_baton,
6539 /* Issue #2915: Raise an error describing the roots of any missing
6540 subtrees, i.e. those that the WC thinks are on disk but have been
6541 removed outside of Subversion. */
6542 if (apr_hash_count(missing_subtrees))
6544 apr_hash_index_t *hi;
6545 svn_stringbuf_t *missing_subtree_err_buf =
6546 svn_stringbuf_create(_("Merge tracking not allowed with missing "
6547 "subtrees; try restoring these items "
6548 "first:\n"), scratch_pool);
6550 for (hi = apr_hash_first(scratch_pool, missing_subtrees);
6552 hi = apr_hash_next(hi))
6554 svn_pool_clear(iterpool);
6555 svn_stringbuf_appendcstr(missing_subtree_err_buf,
6556 svn_dirent_local_style(
6557 apr_hash_this_key(hi), iterpool));
6558 svn_stringbuf_appendcstr(missing_subtree_err_buf, "\n");
6561 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
6562 NULL, missing_subtree_err_buf->data);
6565 if (apr_hash_count(switched_subtrees))
6567 apr_hash_index_t *hi;
6569 for (hi = apr_hash_first(scratch_pool, switched_subtrees);
6571 hi = apr_hash_next(hi))
6573 const char *wc_path = apr_hash_this_key(hi);
6574 svn_client__merge_path_t *child = get_child_with_mergeinfo(
6575 children_with_mergeinfo, wc_path);
6579 child->switched = TRUE;
6583 svn_client__merge_path_t *switched_child =
6584 svn_client__merge_path_create(wc_path, result_pool);
6585 switched_child->switched = TRUE;
6586 insert_child_to_merge(children_with_mergeinfo, switched_child,
6592 if (apr_hash_count(shallow_subtrees))
6594 apr_hash_index_t *hi;
6596 for (hi = apr_hash_first(scratch_pool, shallow_subtrees);
6598 hi = apr_hash_next(hi))
6600 svn_boolean_t new_shallow_child = FALSE;
6601 const char *wc_path = apr_hash_this_key(hi);
6602 svn_depth_t *child_depth = apr_hash_this_val(hi);
6603 svn_client__merge_path_t *shallow_child = get_child_with_mergeinfo(
6604 children_with_mergeinfo, wc_path);
6608 if (*child_depth == svn_depth_empty
6609 || *child_depth == svn_depth_files)
6610 shallow_child->missing_child = TRUE;
6614 shallow_child = svn_client__merge_path_create(wc_path,
6616 new_shallow_child = TRUE;
6618 if (*child_depth == svn_depth_empty
6619 || *child_depth == svn_depth_files)
6620 shallow_child->missing_child = TRUE;
6623 /* A little trickery: If PATH doesn't have any mergeinfo or has
6624 only inheritable mergeinfo, we still describe it as having
6625 non-inheritable mergeinfo if it is missing a child due to
6626 a shallow depth. Why? Because the mergeinfo we'll add to PATH
6627 to describe the merge must be non-inheritable, so PATH's missing
6628 children don't inherit it. Marking these PATHs as non-
6629 inheritable allows the logic for case 3 to properly account
6630 for PATH's children. */
6631 if (!shallow_child->has_noninheritable
6632 && (*child_depth == svn_depth_empty
6633 || *child_depth == svn_depth_files))
6635 shallow_child->has_noninheritable = TRUE;
6638 if (new_shallow_child)
6639 insert_child_to_merge(children_with_mergeinfo, shallow_child,
6644 /* Case 6: Paths absent from disk due to server or user exclusion. */
6645 SVN_ERR(svn_wc__get_excluded_subtrees(&excluded_subtrees,
6646 ctx->wc_ctx, target->abspath,
6647 result_pool, scratch_pool));
6648 if (excluded_subtrees)
6650 apr_hash_index_t *hi;
6652 for (hi = apr_hash_first(scratch_pool, excluded_subtrees);
6654 hi = apr_hash_next(hi))
6656 const char *wc_path = apr_hash_this_key(hi);
6657 svn_client__merge_path_t *child = get_child_with_mergeinfo(
6658 children_with_mergeinfo, wc_path);
6662 child->absent = TRUE;
6666 svn_client__merge_path_t *absent_child =
6667 svn_client__merge_path_create(wc_path, result_pool);
6668 absent_child->absent = TRUE;
6669 insert_child_to_merge(children_with_mergeinfo, absent_child,
6675 /* Case 7: The merge target MERGE_CMD_BATON->target->abspath is always
6677 if (!get_child_with_mergeinfo(children_with_mergeinfo,
6680 svn_client__merge_path_t *target_child =
6681 svn_client__merge_path_create(target->abspath,
6683 insert_child_to_merge(children_with_mergeinfo, target_child,
6687 /* Case 8: Path is an immediate *directory* child of
6688 MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_immediates.
6690 Case 9: Path is an immediate *file* child of
6691 MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_files. */
6692 if (depth == svn_depth_immediates || depth == svn_depth_files)
6695 const apr_array_header_t *immediate_children;
6697 SVN_ERR(svn_wc__node_get_children_of_working_node(
6698 &immediate_children, ctx->wc_ctx,
6699 target->abspath, scratch_pool, scratch_pool));
6701 for (j = 0; j < immediate_children->nelts; j++)
6703 const char *immediate_child_abspath =
6704 APR_ARRAY_IDX(immediate_children, j, const char *);
6705 svn_node_kind_t immediate_child_kind;
6707 svn_pool_clear(iterpool);
6708 SVN_ERR(svn_wc_read_kind2(&immediate_child_kind,
6709 ctx->wc_ctx, immediate_child_abspath,
6710 FALSE, FALSE, iterpool));
6711 if ((immediate_child_kind == svn_node_dir
6712 && depth == svn_depth_immediates)
6713 || (immediate_child_kind == svn_node_file
6714 && depth == svn_depth_files))
6716 if (!get_child_with_mergeinfo(children_with_mergeinfo,
6717 immediate_child_abspath))
6719 svn_client__merge_path_t *immediate_child =
6720 svn_client__merge_path_create(immediate_child_abspath,
6723 if (immediate_child_kind == svn_node_dir
6724 && depth == svn_depth_immediates)
6725 immediate_child->immediate_child_dir = TRUE;
6727 insert_child_to_merge(children_with_mergeinfo,
6728 immediate_child, result_pool);
6734 /* If DEPTH isn't empty then cover cases 3), 4), and 5), possibly adding
6735 elements to CHILDREN_WITH_MERGEINFO. */
6736 if (depth <= svn_depth_empty)
6737 return SVN_NO_ERROR;
6739 for (i = 0; i < children_with_mergeinfo->nelts; i++)
6741 svn_client__merge_path_t *child =
6742 APR_ARRAY_IDX(children_with_mergeinfo, i,
6743 svn_client__merge_path_t *);
6744 svn_pool_clear(iterpool);
6746 /* Case 3) Where merging to a path with a switched child the path
6747 gets non-inheritable mergeinfo for the merge range performed and
6748 the child gets its own set of mergeinfo. If the switched child
6749 later "returns", e.g. a switched path is unswitched, the child
6750 may not have any explicit mergeinfo. If the initial merge is
6751 repeated we don't want to repeat the merge for the path, but we
6752 do want to repeat it for the previously switched child. To
6753 ensure this we check if all of CHILD's non-missing children have
6754 explicit mergeinfo (they should already be present in
6755 CHILDREN_WITH_MERGEINFO if they do). If not,
6756 add the children without mergeinfo to CHILDREN_WITH_MERGEINFO so
6757 do_directory_merge() will merge them independently.
6759 But that's not enough! Since do_directory_merge() performs
6760 the merges on the paths in CHILDREN_WITH_MERGEINFO in a depth
6761 first manner it will merge the previously switched path's parent
6762 first. As part of this merge it will update the parent's
6763 previously non-inheritable mergeinfo and make it inheritable
6764 (since it notices the path has no missing children), then when
6765 do_directory_merge() finally merges the previously missing
6766 child it needs to get mergeinfo from the child's nearest
6767 ancestor, but since do_directory_merge() already tweaked that
6768 mergeinfo, removing the non-inheritable flag, it appears that the
6769 child already has been merged to. To prevent this we set
6770 override mergeinfo on the child now, before any merging is done,
6771 so it has explicit mergeinfo that reflects only CHILD's
6772 inheritable mergeinfo. */
6774 /* If depth is immediates or files then don't add new children if
6775 CHILD is a subtree of the merge target; those children are below
6776 the operational depth of the merge. */
6777 if (child->has_noninheritable
6778 && (i == 0 || depth == svn_depth_infinity))
6780 const apr_array_header_t *children;
6783 SVN_ERR(svn_wc__node_get_children_of_working_node(
6787 iterpool, iterpool));
6788 for (j = 0; j < children->nelts; j++)
6790 svn_client__merge_path_t *child_of_noninheritable;
6791 const char *child_abspath = APR_ARRAY_IDX(children, j,
6794 /* Does this child already exist in CHILDREN_WITH_MERGEINFO?
6795 If not, create it and insert it into
6796 CHILDREN_WITH_MERGEINFO and set override mergeinfo on
6798 child_of_noninheritable =
6799 get_child_with_mergeinfo(children_with_mergeinfo,
6801 if (!child_of_noninheritable)
6803 /* Don't add directory children if DEPTH
6804 is svn_depth_files. */
6805 if (depth == svn_depth_files)
6807 svn_node_kind_t child_kind;
6808 SVN_ERR(svn_wc_read_kind2(&child_kind,
6809 ctx->wc_ctx, child_abspath,
6810 FALSE, FALSE, iterpool));
6811 if (child_kind != svn_node_file)
6814 /* else DEPTH is infinity or immediates so we want both
6815 directory and file children. */
6817 child_of_noninheritable =
6818 svn_client__merge_path_create(child_abspath, result_pool);
6819 child_of_noninheritable->child_of_noninheritable = TRUE;
6820 insert_child_to_merge(children_with_mergeinfo,
6821 child_of_noninheritable,
6823 if (!dry_run && same_repos)
6825 svn_mergeinfo_t mergeinfo;
6827 SVN_ERR(svn_client__get_wc_mergeinfo(
6829 svn_mergeinfo_nearest_ancestor,
6830 child_of_noninheritable->abspath,
6831 target->abspath, NULL, FALSE,
6832 ctx, iterpool, iterpool));
6834 SVN_ERR(svn_client__record_wc_mergeinfo(
6835 child_of_noninheritable->abspath, mergeinfo,
6836 FALSE, ctx, iterpool));
6841 /* Case 4 and 5 are handled by the following function. */
6842 SVN_ERR(insert_parent_and_sibs_of_sw_absent_del_subtree(
6843 children_with_mergeinfo, target, &i, child,
6844 depth, ctx, result_pool));
6845 } /* i < children_with_mergeinfo->nelts */
6846 svn_pool_destroy(iterpool);
6848 return SVN_NO_ERROR;
6852 /* Implements the svn_log_entry_receiver_t interface.
6854 * BATON is an 'apr_array_header_t *' array of 'svn_revnum_t'.
6855 * Push a copy of LOG_ENTRY->revision onto BATON. Thus, a
6856 * series of invocations of this callback accumulates the
6857 * corresponding set of revisions into BATON.
6859 static svn_error_t *
6860 log_changed_revs(void *baton,
6861 svn_log_entry_t *log_entry,
6864 apr_array_header_t *revs = baton;
6866 APR_ARRAY_PUSH(revs, svn_revnum_t) = log_entry->revision;
6867 return SVN_NO_ERROR;
6871 /* Set *MIN_REV_P to the oldest and *MAX_REV_P to the youngest start or end
6872 * revision occurring in RANGELIST, or to SVN_INVALID_REVNUM if RANGELIST
6875 merge_range_find_extremes(svn_revnum_t *min_rev_p,
6876 svn_revnum_t *max_rev_p,
6877 const svn_rangelist_t *rangelist)
6881 *min_rev_p = SVN_INVALID_REVNUM;
6882 *max_rev_p = SVN_INVALID_REVNUM;
6883 for (i = 0; i < rangelist->nelts; i++)
6885 svn_merge_range_t *range
6886 = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
6887 svn_revnum_t range_min = MIN(range->start, range->end);
6888 svn_revnum_t range_max = MAX(range->start, range->end);
6890 if ((! SVN_IS_VALID_REVNUM(*min_rev_p)) || (range_min < *min_rev_p))
6891 *min_rev_p = range_min;
6892 if ((! SVN_IS_VALID_REVNUM(*max_rev_p)) || (range_max > *max_rev_p))
6893 *max_rev_p = range_max;
6897 /* Wrapper around svn_ra_get_log2(). Invoke RECEIVER with RECEIVER_BATON
6898 * on each commit from YOUNGEST_REV to OLDEST_REV in which TARGET_RELPATH
6899 * changed. TARGET_RELPATH is relative to RA_SESSION's URL.
6900 * Important: Revision properties are not retrieved by this function for
6901 * performance reasons.
6903 static svn_error_t *
6904 get_log(svn_ra_session_t *ra_session,
6905 const char *target_relpath,
6906 svn_revnum_t youngest_rev,
6907 svn_revnum_t oldest_rev,
6908 svn_boolean_t discover_changed_paths,
6909 svn_log_entry_receiver_t receiver,
6910 void *receiver_baton,
6913 apr_array_header_t *log_targets;
6914 apr_array_header_t *revprops;
6916 log_targets = apr_array_make(pool, 1, sizeof(const char *));
6917 APR_ARRAY_PUSH(log_targets, const char *) = target_relpath;
6919 revprops = apr_array_make(pool, 0, sizeof(const char *));
6921 SVN_ERR(svn_ra_get_log2(ra_session, log_targets, youngest_rev,
6922 oldest_rev, 0 /* limit */, discover_changed_paths,
6923 FALSE /* strict_node_history */,
6924 FALSE /* include_merged_revisions */,
6925 revprops, receiver, receiver_baton, pool));
6927 return SVN_NO_ERROR;
6930 /* Set *OPERATIVE_RANGES_P to an array of svn_merge_range_t * merge
6931 range objects copied wholesale from RANGES which have the property
6932 that in some revision within that range the object identified by
6933 RA_SESSION was modified (if by "modified" we mean "'svn log' would
6934 return that revision). *OPERATIVE_RANGES_P is allocated from the
6935 same pool as RANGES, and the ranges within it are shared with
6938 *OPERATIVE_RANGES_P may be the same as RANGES (that is, the output
6939 parameter is set only after the input is no longer used).
6941 Use POOL for temporary allocations. */
6942 static svn_error_t *
6943 remove_noop_merge_ranges(svn_rangelist_t **operative_ranges_p,
6944 svn_ra_session_t *ra_session,
6945 const svn_rangelist_t *ranges,
6949 svn_revnum_t oldest_rev, youngest_rev;
6950 apr_array_header_t *changed_revs =
6951 apr_array_make(pool, ranges->nelts, sizeof(svn_revnum_t));
6952 svn_rangelist_t *operative_ranges =
6953 apr_array_make(ranges->pool, ranges->nelts, ranges->elt_size);
6955 /* Find the revision extremes of the RANGES we have. */
6956 merge_range_find_extremes(&oldest_rev, &youngest_rev, ranges);
6957 if (SVN_IS_VALID_REVNUM(oldest_rev))
6958 oldest_rev++; /* make it inclusive */
6960 /* Get logs across those ranges, recording which revisions hold
6961 changes to our object's history. */
6962 SVN_ERR(get_log(ra_session, "", youngest_rev, oldest_rev, FALSE,
6963 log_changed_revs, changed_revs, pool));
6965 /* Are there *any* changes? */
6966 if (changed_revs->nelts)
6968 /* Our list of changed revisions should be in youngest-to-oldest
6970 svn_revnum_t youngest_changed_rev
6971 = APR_ARRAY_IDX(changed_revs, 0, svn_revnum_t);
6972 svn_revnum_t oldest_changed_rev
6973 = APR_ARRAY_IDX(changed_revs, changed_revs->nelts - 1, svn_revnum_t);
6975 /* Now, copy from RANGES to *OPERATIVE_RANGES, filtering out ranges
6976 that aren't operative (by virtue of not having any revisions
6977 represented in the CHANGED_REVS array). */
6978 for (i = 0; i < ranges->nelts; i++)
6980 svn_merge_range_t *range = APR_ARRAY_IDX(ranges, i,
6981 svn_merge_range_t *);
6982 svn_revnum_t range_min = MIN(range->start, range->end) + 1;
6983 svn_revnum_t range_max = MAX(range->start, range->end);
6986 /* If the merge range is entirely outside the range of changed
6987 revisions, we've no use for it. */
6988 if ((range_min > youngest_changed_rev)
6989 || (range_max < oldest_changed_rev))
6992 /* Walk through the changed_revs to see if any of them fall
6993 inside our current range. */
6994 for (j = 0; j < changed_revs->nelts; j++)
6996 svn_revnum_t changed_rev
6997 = APR_ARRAY_IDX(changed_revs, j, svn_revnum_t);
6998 if ((changed_rev >= range_min) && (changed_rev <= range_max))
7000 APR_ARRAY_PUSH(operative_ranges, svn_merge_range_t *) =
7008 *operative_ranges_p = operative_ranges;
7009 return SVN_NO_ERROR;
7013 /*-----------------------------------------------------------------------*/
7015 /*** Merge Source Normalization ***/
7017 /* qsort-compatible sort routine, rating merge_source_t * objects to
7018 be in descending (youngest-to-oldest) order based on their ->loc1->rev
7021 compare_merge_source_ts(const void *a,
7024 svn_revnum_t a_rev = (*(const merge_source_t *const *)a)->loc1->rev;
7025 svn_revnum_t b_rev = (*(const merge_source_t *const *)b)->loc1->rev;
7028 return a_rev < b_rev ? 1 : -1;
7031 /* Set *MERGE_SOURCE_TS_P to a list of merge sources generated by
7032 slicing history location SEGMENTS with a given requested merge
7033 RANGE. Use SOURCE_LOC for full source URL calculation.
7035 Order the merge sources in *MERGE_SOURCE_TS_P from oldest to
7037 static svn_error_t *
7038 combine_range_with_segments(apr_array_header_t **merge_source_ts_p,
7039 const svn_merge_range_t *range,
7040 const apr_array_header_t *segments,
7041 const svn_client__pathrev_t *source_loc,
7044 apr_array_header_t *merge_source_ts =
7045 apr_array_make(pool, 1, sizeof(merge_source_t *));
7046 svn_revnum_t minrev = MIN(range->start, range->end) + 1;
7047 svn_revnum_t maxrev = MAX(range->start, range->end);
7048 svn_boolean_t subtractive = (range->start > range->end);
7051 for (i = 0; i < segments->nelts; i++)
7053 svn_location_segment_t *segment =
7054 APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
7055 svn_client__pathrev_t *loc1, *loc2;
7056 merge_source_t *merge_source;
7057 const char *path1 = NULL;
7060 /* If this segment doesn't overlap our range at all, or
7061 represents a gap, ignore it. */
7062 if ((segment->range_end < minrev)
7063 || (segment->range_start > maxrev)
7064 || (! segment->path))
7067 /* If our range spans a segment boundary, we have to point our
7068 merge_source_t's path1 to the path of the immediately older
7069 segment, else it points to the same location as its path2. */
7070 rev1 = MAX(segment->range_start, minrev) - 1;
7071 if (minrev <= segment->range_start)
7075 path1 = (APR_ARRAY_IDX(segments, i - 1,
7076 svn_location_segment_t *))->path;
7078 /* If we've backed PATH1 up into a segment gap, let's back
7079 it up further still to the segment before the gap. We'll
7080 have to adjust rev1, too. */
7081 if ((! path1) && (i > 1))
7083 path1 = (APR_ARRAY_IDX(segments, i - 2,
7084 svn_location_segment_t *))->path;
7085 rev1 = (APR_ARRAY_IDX(segments, i - 2,
7086 svn_location_segment_t *))->range_end;
7091 path1 = apr_pstrdup(pool, segment->path);
7094 /* If we don't have two valid paths, we won't know what to do
7095 when merging. This could happen if someone requested a merge
7096 where the source didn't exist in a particular revision or
7097 something. The merge code would probably bomb out anyway, so
7098 we'll just *not* create a merge source in this case. */
7099 if (! (path1 && segment->path))
7102 /* Build our merge source structure. */
7103 loc1 = svn_client__pathrev_create_with_relpath(
7104 source_loc->repos_root_url, source_loc->repos_uuid,
7106 loc2 = svn_client__pathrev_create_with_relpath(
7107 source_loc->repos_root_url, source_loc->repos_uuid,
7108 MIN(segment->range_end, maxrev), segment->path, pool);
7109 /* If this is subtractive, reverse the whole calculation. */
7111 merge_source = merge_source_create(loc2, loc1, TRUE /* ancestral */,
7114 merge_source = merge_source_create(loc1, loc2, TRUE /* ancestral */,
7117 APR_ARRAY_PUSH(merge_source_ts, merge_source_t *) = merge_source;
7120 /* If this was a subtractive merge, and we created more than one
7121 merge source, we need to reverse the sort ordering of our sources. */
7122 if (subtractive && (merge_source_ts->nelts > 1))
7123 qsort(merge_source_ts->elts, merge_source_ts->nelts,
7124 merge_source_ts->elt_size, compare_merge_source_ts);
7126 *merge_source_ts_p = merge_source_ts;
7127 return SVN_NO_ERROR;
7130 /* Similar to normalize_merge_sources() except the input MERGE_RANGE_TS is a
7133 static svn_error_t *
7134 normalize_merge_sources_internal(apr_array_header_t **merge_sources_p,
7135 const svn_client__pathrev_t *source_loc,
7136 const svn_rangelist_t *merge_range_ts,
7137 svn_ra_session_t *ra_session,
7138 svn_client_ctx_t *ctx,
7139 apr_pool_t *result_pool,
7140 apr_pool_t *scratch_pool)
7142 svn_revnum_t source_peg_revnum = source_loc->rev;
7143 svn_revnum_t oldest_requested, youngest_requested;
7144 svn_revnum_t trim_revision = SVN_INVALID_REVNUM;
7145 apr_array_header_t *segments;
7148 /* Initialize our return variable. */
7149 *merge_sources_p = apr_array_make(result_pool, 1, sizeof(merge_source_t *));
7151 /* No ranges to merge? No problem. */
7152 if (merge_range_ts->nelts == 0)
7153 return SVN_NO_ERROR;
7155 /* Find the extremes of the revisions across our set of ranges. */
7156 merge_range_find_extremes(&oldest_requested, &youngest_requested,
7159 /* ### FIXME: Our underlying APIs can't yet handle the case where
7160 the peg revision isn't the youngest of the three revisions. So
7161 we'll just verify that the source in the peg revision is related
7162 to the source in the youngest requested revision (which is
7163 all the underlying APIs would do in this case right now anyway). */
7164 if (source_peg_revnum < youngest_requested)
7166 svn_client__pathrev_t *start_loc;
7168 SVN_ERR(svn_client__repos_location(&start_loc,
7169 ra_session, source_loc,
7171 ctx, scratch_pool, scratch_pool));
7172 source_peg_revnum = youngest_requested;
7175 /* Fetch the locations for our merge range span. */
7176 SVN_ERR(svn_client__repos_location_segments(&segments,
7177 ra_session, source_loc->url,
7183 /* See if we fetched enough history to do the job. "Surely we did,"
7184 you say. "After all, we covered the entire requested merge
7185 range." Yes, that's true, but if our first segment doesn't
7186 extend back to the oldest request revision, we've got a special
7187 case to deal with. Or if the first segment represents a gap,
7188 that's another special case. */
7189 trim_revision = SVN_INVALID_REVNUM;
7190 if (segments->nelts)
7192 svn_location_segment_t *first_segment =
7193 APR_ARRAY_IDX(segments, 0, svn_location_segment_t *);
7195 /* If the first segment doesn't start with the OLDEST_REQUESTED
7196 revision, we'll need to pass a trim revision to our range
7198 if (first_segment->range_start != oldest_requested)
7200 trim_revision = first_segment->range_start;
7203 /* Else, if the first segment has no path (and therefore is a
7204 gap), then we'll fetch the copy source revision from the
7205 second segment (provided there is one, of course) and use it
7206 to prepend an extra pathful segment to our list.
7208 ### We could avoid this bit entirely if we'd passed
7209 ### SVN_INVALID_REVNUM instead of OLDEST_REQUESTED to
7210 ### svn_client__repos_location_segments(), but that would
7211 ### really penalize clients hitting pre-1.5 repositories with
7212 ### the typical small merge range request (because of the
7213 ### lack of a node-origins cache in the repository). */
7214 else if (! first_segment->path)
7216 if (segments->nelts > 1)
7218 svn_location_segment_t *second_segment =
7219 APR_ARRAY_IDX(segments, 1, svn_location_segment_t *);
7220 const char *segment_url;
7221 const char *original_repos_relpath;
7222 svn_revnum_t original_revision;
7223 svn_opt_revision_t range_start_rev;
7224 range_start_rev.kind = svn_opt_revision_number;
7225 range_start_rev.value.number = second_segment->range_start;
7227 segment_url = svn_path_url_add_component2(
7228 source_loc->repos_root_url, second_segment->path,
7230 SVN_ERR(svn_client__get_copy_source(&original_repos_relpath,
7235 result_pool, scratch_pool));
7236 /* Got copyfrom data? Fix up the first segment to cover
7237 back to COPYFROM_REV + 1, and then prepend a new
7238 segment covering just COPYFROM_REV. */
7239 if (original_repos_relpath)
7241 svn_location_segment_t *new_segment =
7242 apr_pcalloc(result_pool, sizeof(*new_segment));
7244 new_segment->path = original_repos_relpath;
7245 new_segment->range_start = original_revision;
7246 new_segment->range_end = original_revision;
7247 svn_sort__array_insert(segments, &new_segment, 0);
7253 /* For each range in our requested range set, try to determine the
7254 path(s) associated with that range. */
7255 for (i = 0; i < merge_range_ts->nelts; i++)
7257 svn_merge_range_t *range =
7258 APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *);
7259 apr_array_header_t *merge_sources;
7261 if (SVN_IS_VALID_REVNUM(trim_revision))
7263 /* If the range predates the trim revision, discard it. */
7264 if (MAX(range->start, range->end) < trim_revision)
7267 /* If the range overlaps the trim revision, trim it. */
7268 if (range->start < trim_revision)
7269 range->start = trim_revision;
7270 if (range->end < trim_revision)
7271 range->end = trim_revision;
7274 /* Copy the resulting merge sources into master list thereof. */
7275 SVN_ERR(combine_range_with_segments(&merge_sources, range,
7276 segments, source_loc,
7278 apr_array_cat(*merge_sources_p, merge_sources);
7281 return SVN_NO_ERROR;
7284 /* Determine the normalized ranges to merge from a given line of history.
7286 Calculate the result by intersecting the list of location segments at
7287 which SOURCE_LOC existed along its line of history with the requested
7288 revision ranges in RANGES_TO_MERGE. RANGES_TO_MERGE is an array of
7289 (svn_opt_revision_range_t *) revision ranges. Use SOURCE_PATH_OR_URL to
7290 resolve any WC-relative revision specifiers (such as 'base') in
7293 Set *MERGE_SOURCES_P to an array of merge_source_t * objects, each
7294 describing a normalized range of revisions to be merged from the line
7295 history of SOURCE_LOC. Order the objects from oldest to youngest.
7297 RA_SESSION is an RA session open to the repository of SOURCE_LOC; it may
7298 be temporarily reparented within this function. Use RA_SESSION to find
7299 the location segments along the line of history of SOURCE_LOC.
7301 Allocate MERGE_SOURCES_P and its contents in RESULT_POOL.
7303 See `MERGEINFO MERGE SOURCE NORMALIZATION' for more on the
7304 background of this function.
7306 static svn_error_t *
7307 normalize_merge_sources(apr_array_header_t **merge_sources_p,
7308 const char *source_path_or_url,
7309 const svn_client__pathrev_t *source_loc,
7310 const apr_array_header_t *ranges_to_merge,
7311 svn_ra_session_t *ra_session,
7312 svn_client_ctx_t *ctx,
7313 apr_pool_t *result_pool,
7314 apr_pool_t *scratch_pool)
7316 const char *source_abspath_or_url;
7317 svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
7318 svn_rangelist_t *merge_range_ts;
7320 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7322 if(!svn_path_is_url(source_path_or_url))
7323 SVN_ERR(svn_dirent_get_absolute(&source_abspath_or_url, source_path_or_url,
7326 source_abspath_or_url = source_path_or_url;
7328 /* Create a list to hold svn_merge_range_t's. */
7329 merge_range_ts = apr_array_make(scratch_pool, ranges_to_merge->nelts,
7330 sizeof(svn_merge_range_t *));
7332 for (i = 0; i < ranges_to_merge->nelts; i++)
7334 svn_opt_revision_range_t *range
7335 = APR_ARRAY_IDX(ranges_to_merge, i, svn_opt_revision_range_t *);
7336 svn_merge_range_t mrange;
7338 svn_pool_clear(iterpool);
7340 /* Resolve revisions to real numbers, validating as we go. */
7341 if ((range->start.kind == svn_opt_revision_unspecified)
7342 || (range->end.kind == svn_opt_revision_unspecified))
7343 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
7344 _("Not all required revisions are specified"));
7346 SVN_ERR(svn_client__get_revision_number(&mrange.start, &youngest_rev,
7348 source_abspath_or_url,
7349 ra_session, &range->start,
7351 SVN_ERR(svn_client__get_revision_number(&mrange.end, &youngest_rev,
7353 source_abspath_or_url,
7354 ra_session, &range->end,
7357 /* If this isn't a no-op range... */
7358 if (mrange.start != mrange.end)
7360 /* ...then add it to the list. */
7361 mrange.inheritable = TRUE;
7362 APR_ARRAY_PUSH(merge_range_ts, svn_merge_range_t *)
7363 = svn_merge_range_dup(&mrange, scratch_pool);
7367 SVN_ERR(normalize_merge_sources_internal(
7368 merge_sources_p, source_loc,
7369 merge_range_ts, ra_session, ctx, result_pool, scratch_pool));
7371 svn_pool_destroy(iterpool);
7372 return SVN_NO_ERROR;
7376 /*-----------------------------------------------------------------------*/
7378 /*** Merge Workhorse Functions ***/
7380 /* Helper for do_directory_merge() and do_file_merge() which filters out a
7381 path's own natural history from the mergeinfo describing a merge.
7383 Given the natural history IMPLICIT_MERGEINFO of some wc merge target path,
7384 the repository-relative merge source path SOURCE_REL_PATH, and the
7385 requested merge range REQUESTED_RANGE from SOURCE_REL_PATH, remove any
7386 portion of REQUESTED_RANGE which is already described in
7387 IMPLICIT_MERGEINFO. Store the result in *FILTERED_RANGELIST.
7389 This function only filters natural history for mergeinfo that will be
7390 *added* during a forward merge. Removing natural history from explicit
7391 mergeinfo is harmless. If REQUESTED_RANGE describes a reverse merge,
7392 then *FILTERED_RANGELIST is simply populated with one range described
7393 by REQUESTED_RANGE. *FILTERED_RANGELIST is never NULL.
7395 Allocate *FILTERED_RANGELIST in POOL. */
7396 static svn_error_t *
7397 filter_natural_history_from_mergeinfo(svn_rangelist_t **filtered_rangelist,
7398 const char *source_rel_path,
7399 svn_mergeinfo_t implicit_mergeinfo,
7400 svn_merge_range_t *requested_range,
7403 /* Make the REQUESTED_RANGE into a rangelist. */
7404 svn_rangelist_t *requested_rangelist =
7405 svn_rangelist__initialize(requested_range->start, requested_range->end,
7406 requested_range->inheritable, pool);
7408 *filtered_rangelist = NULL;
7410 /* For forward merges: If the IMPLICIT_MERGEINFO already describes ranges
7411 associated with SOURCE_REL_PATH then filter those ranges out. */
7412 if (implicit_mergeinfo
7413 && (requested_range->start < requested_range->end))
7415 svn_rangelist_t *implied_rangelist =
7416 svn_hash_gets(implicit_mergeinfo, source_rel_path);
7418 if (implied_rangelist)
7419 SVN_ERR(svn_rangelist_remove(filtered_rangelist,
7421 requested_rangelist,
7425 /* If no filtering was performed the filtered rangelist is
7426 simply the requested rangelist.*/
7427 if (! (*filtered_rangelist))
7428 *filtered_rangelist = requested_rangelist;
7430 return SVN_NO_ERROR;
7433 /* Return a merge source representing the sub-range from START_REV to
7434 END_REV of SOURCE. SOURCE obeys the rules described in the
7435 'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file.
7436 The younger of START_REV and END_REV is inclusive while the older is
7439 Allocate the result structure in POOL but leave the URLs in it as shallow
7440 copies of the URLs in SOURCE.
7442 static merge_source_t *
7443 subrange_source(const merge_source_t *source,
7444 svn_revnum_t start_rev,
7445 svn_revnum_t end_rev,
7448 svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
7449 svn_boolean_t same_urls = (strcmp(source->loc1->url, source->loc2->url) == 0);
7450 svn_client__pathrev_t loc1 = *source->loc1;
7451 svn_client__pathrev_t loc2 = *source->loc2;
7453 /* For this function we require that the input source is 'ancestral'. */
7454 SVN_ERR_ASSERT_NO_RETURN(source->ancestral);
7455 SVN_ERR_ASSERT_NO_RETURN(start_rev != end_rev);
7457 loc1.rev = start_rev;
7461 if (is_rollback && (end_rev != source->loc2->rev))
7463 loc2.url = source->loc1->url;
7465 if ((! is_rollback) && (start_rev != source->loc1->rev))
7467 loc1.url = source->loc2->url;
7470 return merge_source_create(&loc1, &loc2, source->ancestral, pool);
7473 /* The single-file, simplified version of do_directory_merge(), which see for
7474 parameter descriptions.
7476 Additional parameters:
7478 If SOURCES_RELATED is set, the "left" and "right" sides of SOURCE are
7479 historically related (ancestors, uncles, second
7480 cousins thrice removed, etc...). (This is used to simulate the
7481 history checks that the repository logic does in the directory case.)
7483 If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
7484 is not NULL, then don't record the new mergeinfo on the TARGET_ABSPATH,
7485 but instead record it in RESULT_CATALOG, where the key is TARGET_ABSPATH
7486 and the value is the new mergeinfo for that path. Allocate additions
7487 to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
7489 CONFLICTED_RANGE is as documented for do_directory_merge().
7491 Note: MERGE_B->RA_SESSION1 must be associated with SOURCE->loc1->url and
7492 MERGE_B->RA_SESSION2 with SOURCE->loc2->url.
7494 static svn_error_t *
7495 do_file_merge(svn_mergeinfo_catalog_t result_catalog,
7496 single_range_conflict_report_t **conflict_report,
7497 const merge_source_t *source,
7498 const char *target_abspath,
7499 const svn_diff_tree_processor_t *processor,
7500 svn_boolean_t sources_related,
7501 svn_boolean_t squelch_mergeinfo_notifications,
7502 merge_cmd_baton_t *merge_b,
7503 apr_pool_t *result_pool,
7504 apr_pool_t *scratch_pool)
7506 svn_rangelist_t *remaining_ranges;
7507 svn_client_ctx_t *ctx = merge_b->ctx;
7508 svn_merge_range_t range;
7509 svn_mergeinfo_t target_mergeinfo;
7510 svn_boolean_t inherited = FALSE;
7511 svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
7512 const svn_client__pathrev_t *primary_src
7513 = is_rollback ? source->loc1 : source->loc2;
7514 svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
7515 svn_client__merge_path_t *merge_target = NULL;
7516 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7518 SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
7520 *conflict_report = NULL;
7522 /* Note that this is a single-file merge. */
7523 range.start = source->loc1->rev;
7524 range.end = source->loc2->rev;
7525 range.inheritable = TRUE;
7527 merge_target = svn_client__merge_path_create(target_abspath, scratch_pool);
7529 if (honor_mergeinfo)
7533 /* Fetch mergeinfo. */
7534 err = get_full_mergeinfo(&target_mergeinfo,
7535 &(merge_target->implicit_mergeinfo),
7536 &inherited, svn_mergeinfo_inherited,
7537 merge_b->ra_session1, target_abspath,
7538 MAX(source->loc1->rev, source->loc2->rev),
7539 MIN(source->loc1->rev, source->loc2->rev),
7540 ctx, scratch_pool, iterpool);
7544 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
7546 err = svn_error_createf(
7547 SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
7548 _("Invalid mergeinfo detected on merge target '%s', "
7549 "merge tracking not possible"),
7550 svn_dirent_local_style(target_abspath, scratch_pool));
7552 return svn_error_trace(err);
7555 /* Calculate remaining merges unless this is a record only merge.
7556 In that case the remaining range is the whole range described
7557 by SOURCE->rev1:rev2. */
7558 if (!merge_b->record_only)
7560 /* ### Bug? calculate_remaining_ranges() needs 'source' to adhere
7561 * to the requirements of 'MERGEINFO MERGE SOURCE NORMALIZATION'
7562 * here, but it doesn't appear to be guaranteed so. */
7563 SVN_ERR(calculate_remaining_ranges(NULL, merge_target,
7566 merge_b->implicit_src_gap, FALSE,
7567 merge_b->ra_session1,
7570 remaining_ranges = merge_target->remaining_ranges;
7572 /* We are honoring mergeinfo and this is not a simple record only
7573 merge which blindly records mergeinfo describing the merge of
7574 SOURCE->LOC1->URL@SOURCE->LOC1->REV through
7575 SOURCE->LOC2->URL@SOURCE->LOC2->REV. This means that the oldest
7576 and youngest revisions merged (as determined above by
7577 calculate_remaining_ranges) might differ from those described
7578 in SOURCE. To keep the '--- Merging *' notifications consistent
7579 with the '--- Recording mergeinfo *' notifications, we adjust
7580 RANGE to account for such changes. */
7581 if (remaining_ranges->nelts)
7583 svn_merge_range_t *adj_start_range =
7584 APR_ARRAY_IDX(remaining_ranges, 0, svn_merge_range_t *);
7585 svn_merge_range_t *adj_end_range =
7586 APR_ARRAY_IDX(remaining_ranges, remaining_ranges->nelts - 1,
7587 svn_merge_range_t *);
7588 range.start = adj_start_range->start;
7589 range.end = adj_end_range->end;
7594 /* The simple cases where our remaining range is SOURCE->rev1:rev2. */
7595 if (!honor_mergeinfo || merge_b->record_only)
7597 remaining_ranges = apr_array_make(scratch_pool, 1, sizeof(&range));
7598 APR_ARRAY_PUSH(remaining_ranges, svn_merge_range_t *) = ⦥
7601 if (!merge_b->record_only)
7603 svn_rangelist_t *ranges_to_merge = apr_array_copy(scratch_pool,
7605 const char *target_relpath = ""; /* relative to root of merge */
7607 if (source->ancestral)
7609 apr_array_header_t *child_with_mergeinfo;
7610 svn_client__merge_path_t *target_info;
7612 /* If we have ancestrally related sources and more than one
7613 range to merge, eliminate no-op ranges before going through
7614 the effort of downloading the many copies of the file
7615 required to do these merges (two copies per range). */
7616 if (remaining_ranges->nelts > 1)
7618 const char *old_sess_url;
7621 SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url,
7622 merge_b->ra_session1,
7625 err = remove_noop_merge_ranges(&ranges_to_merge,
7626 merge_b->ra_session1,
7627 remaining_ranges, scratch_pool);
7628 SVN_ERR(svn_error_compose_create(
7629 err, svn_ra_reparent(merge_b->ra_session1,
7630 old_sess_url, iterpool)));
7633 /* To support notify_merge_begin() initialize our
7634 CHILD_WITH_MERGEINFO. See the comment
7635 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */
7637 child_with_mergeinfo = apr_array_make(scratch_pool, 1,
7638 sizeof(svn_client__merge_path_t *));
7640 /* ### Create a fake copy of merge_target as we don't keep
7641 remaining_ranges in sync (yet). */
7642 target_info = apr_pcalloc(scratch_pool, sizeof(*target_info));
7644 target_info->abspath = merge_target->abspath;
7645 target_info->remaining_ranges = ranges_to_merge;
7647 APR_ARRAY_PUSH(child_with_mergeinfo, svn_client__merge_path_t *)
7650 /* And store in baton to allow using it from notify_merge_begin() */
7651 merge_b->notify_begin.nodes_with_mergeinfo = child_with_mergeinfo;
7654 while (ranges_to_merge->nelts > 0)
7656 svn_merge_range_t *r = APR_ARRAY_IDX(ranges_to_merge, 0,
7657 svn_merge_range_t *);
7658 const merge_source_t *real_source;
7659 const char *left_file, *right_file;
7660 apr_hash_t *left_props, *right_props;
7661 const svn_diff_source_t *left_source;
7662 const svn_diff_source_t *right_source;
7664 svn_pool_clear(iterpool);
7666 /* Ensure any subsequent drives gets their own notification. */
7667 merge_b->notify_begin.last_abspath = NULL;
7669 /* While we currently don't allow it, in theory we could be
7670 fetching two fulltexts from two different repositories here. */
7671 if (source->ancestral)
7672 real_source = subrange_source(source, r->start, r->end, iterpool);
7674 real_source = source;
7675 SVN_ERR(single_file_merge_get_file(&left_file, &left_props,
7676 merge_b->ra_session1,
7679 iterpool, iterpool));
7680 SVN_ERR(single_file_merge_get_file(&right_file, &right_props,
7681 merge_b->ra_session2,
7684 iterpool, iterpool));
7685 /* Calculate sources for the diff processor */
7686 left_source = svn_diff__source_create(r->start, iterpool);
7687 right_source = svn_diff__source_create(r->end, iterpool);
7690 /* If the sources are related or we're ignoring ancestry in diffs,
7691 do a text-n-props merge; otherwise, do a delete-n-add merge. */
7692 if (! (merge_b->diff_ignore_ancestry || sources_related))
7694 struct merge_dir_baton_t dir_baton;
7698 /* Initialize minimal dir baton to allow calculating 'R'eplace
7699 from 'D'elete + 'A'dd. */
7701 memset(&dir_baton, 0, sizeof(dir_baton));
7702 dir_baton.pool = iterpool;
7703 dir_baton.tree_conflict_reason = CONFLICT_REASON_NONE;
7704 dir_baton.tree_conflict_action = svn_wc_conflict_action_edit;
7705 dir_baton.skip_reason = svn_wc_notify_state_unknown;
7710 SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7712 NULL /* right_source */,
7713 NULL /* copyfrom_source */,
7716 iterpool, iterpool));
7718 SVN_ERR(processor->file_deleted(target_relpath,
7726 /* ...plus add... */
7729 SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7730 NULL /* left_source */,
7732 NULL /* copyfrom_source */,
7735 iterpool, iterpool));
7737 SVN_ERR(processor->file_added(target_relpath,
7738 NULL /* copyfrom_source */,
7740 NULL /* copyfrom_file */,
7742 NULL /* copyfrom_props */,
7747 /* ... equals replace. */
7751 void *file_baton = NULL;
7752 svn_boolean_t skip = FALSE;
7753 apr_array_header_t *propchanges;
7756 /* Deduce property diffs. */
7757 SVN_ERR(svn_prop_diffs(&propchanges, right_props, left_props,
7760 SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7763 NULL /* copyfrom_source */,
7764 NULL /* dir_baton */,
7766 iterpool, iterpool));
7768 SVN_ERR(processor->file_changed(target_relpath,
7775 TRUE /* file changed */,
7782 if (is_path_conflicted_by_merge(merge_b))
7784 merge_source_t *remaining_range = NULL;
7786 if (real_source->loc2->rev != source->loc2->rev)
7787 remaining_range = subrange_source(source,
7788 real_source->loc2->rev,
7791 *conflict_report = single_range_conflict_report_create(
7792 real_source, remaining_range, result_pool);
7794 /* Only record partial mergeinfo if only a partial merge was
7795 performed before a conflict was encountered. */
7800 /* Now delete the just merged range from the hash
7801 (This list is used from notify_merge_begin)
7803 Directory merges use remove_first_range_from_remaining_ranges() */
7804 svn_sort__array_delete(ranges_to_merge, 0, 1);
7806 merge_b->notify_begin.last_abspath = NULL;
7807 } /* !merge_b->record_only */
7809 /* Record updated WC mergeinfo to account for our new merges, minus
7810 any unresolved conflicts and skips. We use the original
7811 REMAINING_RANGES here because we want to record all the requested
7812 merge ranges, include the noop ones. */
7813 if (RECORD_MERGEINFO(merge_b) && remaining_ranges->nelts)
7815 const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
7817 svn_rangelist_t *filtered_rangelist;
7819 /* Filter any ranges from TARGET_WCPATH's own history, there is no
7820 need to record this explicitly in mergeinfo, it is already part
7821 of TARGET_WCPATH's natural history (implicit mergeinfo). */
7822 SVN_ERR(filter_natural_history_from_mergeinfo(
7823 &filtered_rangelist,
7825 merge_target->implicit_mergeinfo,
7829 /* Only record mergeinfo if there is something other than
7830 self-referential mergeinfo, but don't record mergeinfo if
7831 TARGET_WCPATH was skipped. */
7832 if (filtered_rangelist->nelts
7833 && (apr_hash_count(merge_b->skipped_abspaths) == 0))
7835 apr_hash_t *merges = apr_hash_make(iterpool);
7837 /* If merge target has inherited mergeinfo set it before
7838 recording the first merge range. */
7840 SVN_ERR(svn_client__record_wc_mergeinfo(target_abspath,
7845 svn_hash_sets(merges, target_abspath, filtered_rangelist);
7847 if (!squelch_mergeinfo_notifications)
7849 /* Notify that we are recording mergeinfo describing a merge. */
7850 svn_merge_range_t n_range;
7852 SVN_ERR(svn_mergeinfo__get_range_endpoints(
7853 &n_range.end, &n_range.start, merges, iterpool));
7854 n_range.inheritable = TRUE;
7855 notify_mergeinfo_recording(target_abspath, &n_range,
7856 merge_b->ctx, iterpool);
7859 SVN_ERR(update_wc_mergeinfo(result_catalog, target_abspath,
7860 mergeinfo_path, merges, is_rollback,
7865 merge_b->notify_begin.nodes_with_mergeinfo = NULL;
7867 svn_pool_destroy(iterpool);
7869 return SVN_NO_ERROR;
7872 /* Helper for do_directory_merge() to handle the case where a merge editor
7873 drive adds explicit mergeinfo to a path which didn't have any explicit
7874 mergeinfo previously.
7876 MERGE_B is cascaded from the argument of the same
7877 name in do_directory_merge(). Should be called only after
7878 do_directory_merge() has called populate_remaining_ranges() and populated
7879 the remaining_ranges field of each child in
7880 CHILDREN_WITH_MERGEINFO (i.e. the remaining_ranges fields can be
7881 empty but never NULL).
7883 If MERGE_B->DRY_RUN is true do nothing, if it is false then
7884 for each path (if any) in MERGE_B->PATHS_WITH_NEW_MERGEINFO merge that
7885 path's inherited mergeinfo (if any) with its working explicit mergeinfo
7886 and set that as the path's new explicit mergeinfo. Then add an
7887 svn_client__merge_path_t * element representing the path to
7888 CHILDREN_WITH_MERGEINFO if it isn't already present. All fields
7889 in any elements added to CHILDREN_WITH_MERGEINFO are initialized
7890 to FALSE/NULL with the exception of 'path' and 'remaining_ranges'. The
7891 latter is set to a rangelist equal to the remaining_ranges of the path's
7892 nearest path-wise ancestor in CHILDREN_WITH_MERGEINFO.
7894 Any elements added to CHILDREN_WITH_MERGEINFO are allocated
7896 static svn_error_t *
7897 process_children_with_new_mergeinfo(merge_cmd_baton_t *merge_b,
7898 apr_array_header_t *children_with_mergeinfo,
7901 apr_pool_t *iterpool;
7902 apr_hash_index_t *hi;
7904 if (!merge_b->paths_with_new_mergeinfo || merge_b->dry_run)
7905 return SVN_NO_ERROR;
7907 /* Iterate over each path with explicit mergeinfo added by the merge. */
7908 iterpool = svn_pool_create(pool);
7909 for (hi = apr_hash_first(pool, merge_b->paths_with_new_mergeinfo);
7911 hi = apr_hash_next(hi))
7913 const char *abspath_with_new_mergeinfo = apr_hash_this_key(hi);
7914 svn_mergeinfo_t path_inherited_mergeinfo;
7915 svn_mergeinfo_t path_explicit_mergeinfo;
7916 svn_client__merge_path_t *new_child;
7918 svn_pool_clear(iterpool);
7920 /* Note: We could skip recording inherited mergeinfo here if this path
7921 was added (with preexisting mergeinfo) by the merge. That's actually
7922 more correct, since the inherited mergeinfo likely describes
7923 non-existent or unrelated merge history, but it's not quite so simple
7924 as that, see http://subversion.tigris.org/issues/show_bug.cgi?id=4309
7927 /* Get the path's new explicit mergeinfo... */
7928 SVN_ERR(svn_client__get_wc_mergeinfo(&path_explicit_mergeinfo, NULL,
7929 svn_mergeinfo_explicit,
7930 abspath_with_new_mergeinfo,
7933 iterpool, iterpool));
7934 /* ...there *should* always be explicit mergeinfo at this point
7935 but you can't be too careful. */
7936 if (path_explicit_mergeinfo)
7938 /* Get the mergeinfo the path would have inherited before
7940 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(
7941 &path_inherited_mergeinfo,
7944 svn_mergeinfo_nearest_ancestor, /* We only want inherited MI */
7945 merge_b->ra_session2,
7946 abspath_with_new_mergeinfo,
7950 /* If the path inherited any mergeinfo then merge that with the
7951 explicit mergeinfo and record the result as the path's new
7952 explicit mergeinfo. */
7953 if (path_inherited_mergeinfo)
7955 SVN_ERR(svn_mergeinfo_merge2(path_explicit_mergeinfo,
7956 path_inherited_mergeinfo,
7957 iterpool, iterpool));
7958 SVN_ERR(svn_client__record_wc_mergeinfo(
7959 abspath_with_new_mergeinfo,
7960 path_explicit_mergeinfo,
7961 FALSE, merge_b->ctx, iterpool));
7964 /* If the path is not in CHILDREN_WITH_MERGEINFO then add it. */
7966 get_child_with_mergeinfo(children_with_mergeinfo,
7967 abspath_with_new_mergeinfo);
7970 const svn_client__merge_path_t *parent
7971 = find_nearest_ancestor(children_with_mergeinfo,
7972 FALSE, abspath_with_new_mergeinfo);
7974 = svn_client__merge_path_create(abspath_with_new_mergeinfo,
7977 /* If path_with_new_mergeinfo is the merge target itself
7978 then it should already be in
7979 CHILDREN_WITH_MERGEINFO per the criteria of
7980 get_mergeinfo_paths() and we shouldn't be in this block.
7981 If path_with_new_mergeinfo is a subtree then it must have
7982 a parent in CHILDREN_WITH_MERGEINFO if only
7983 the merge target itself...so if we don't find a parent
7984 the caller has done something quite wrong. */
7985 SVN_ERR_ASSERT(parent);
7986 SVN_ERR_ASSERT(parent->remaining_ranges);
7988 /* Set the path's remaining_ranges equal to its parent's. */
7989 new_child->remaining_ranges = svn_rangelist_dup(
7990 parent->remaining_ranges, pool);
7991 insert_child_to_merge(children_with_mergeinfo, new_child, pool);
7995 svn_pool_destroy(iterpool);
7997 return SVN_NO_ERROR;
8000 /* Return true if any path in SUBTREES is equal to, or is a subtree of,
8001 LOCAL_ABSPATH. Return false otherwise. The keys of SUBTREES are
8002 (const char *) absolute paths and its values are irrelevant.
8003 If SUBTREES is NULL return false. */
8004 static svn_boolean_t
8005 path_is_subtree(const char *local_abspath,
8006 apr_hash_t *subtrees,
8011 apr_hash_index_t *hi;
8013 for (hi = apr_hash_first(pool, subtrees);
8014 hi; hi = apr_hash_next(hi))
8016 const char *path_touched_by_merge = apr_hash_this_key(hi);
8017 if (svn_dirent_is_ancestor(local_abspath, path_touched_by_merge))
8024 /* Return true if any merged, skipped, added or tree-conflicted path
8025 recorded in MERGE_B is equal to, or is a subtree of LOCAL_ABSPATH. Return
8028 ### Why not text- or prop-conflicted paths? Are such paths guaranteed
8029 to be recorded as 'merged' or 'skipped' or 'added', perhaps?
8031 static svn_boolean_t
8032 subtree_touched_by_merge(const char *local_abspath,
8033 merge_cmd_baton_t *merge_b,
8036 return (path_is_subtree(local_abspath, merge_b->merged_abspaths, pool)
8037 || path_is_subtree(local_abspath, merge_b->skipped_abspaths, pool)
8038 || path_is_subtree(local_abspath, merge_b->added_abspaths, pool)
8039 || path_is_subtree(local_abspath, merge_b->tree_conflicted_abspaths,
8043 /* Helper for do_directory_merge() when performing mergeinfo unaware merges.
8045 Merge the SOURCE diff into TARGET_DIR_WCPATH.
8047 SOURCE, DEPTH, NOTIFY_B, and MERGE_B
8048 are all cascaded from do_directory_merge's arguments of the same names.
8050 CONFLICT_REPORT is as documented for do_directory_merge().
8052 NOTE: This is a very thin wrapper around drive_merge_report_editor() and
8053 exists only to populate CHILDREN_WITH_MERGEINFO with the single element
8054 expected during mergeinfo unaware merges.
8056 static svn_error_t *
8057 do_mergeinfo_unaware_dir_merge(single_range_conflict_report_t **conflict_report,
8058 const merge_source_t *source,
8059 const char *target_dir_wcpath,
8060 apr_array_header_t *children_with_mergeinfo,
8061 const svn_diff_tree_processor_t *processor,
8063 merge_cmd_baton_t *merge_b,
8064 apr_pool_t *result_pool,
8065 apr_pool_t *scratch_pool)
8067 /* Initialize CHILDREN_WITH_MERGEINFO and populate it with
8068 one element describing the merge of SOURCE->rev1:rev2 to
8069 TARGET_DIR_WCPATH. */
8070 svn_client__merge_path_t *item
8071 = svn_client__merge_path_create(target_dir_wcpath, scratch_pool);
8073 *conflict_report = NULL;
8074 item->remaining_ranges = svn_rangelist__initialize(source->loc1->rev,
8076 TRUE, scratch_pool);
8077 APR_ARRAY_PUSH(children_with_mergeinfo,
8078 svn_client__merge_path_t *) = item;
8079 SVN_ERR(drive_merge_report_editor(target_dir_wcpath,
8081 NULL, processor, depth,
8082 merge_b, scratch_pool));
8083 if (is_path_conflicted_by_merge(merge_b))
8085 *conflict_report = single_range_conflict_report_create(
8086 source, NULL, result_pool);
8088 return SVN_NO_ERROR;
8091 /* A svn_log_entry_receiver_t baton for log_find_operative_subtree_revs(). */
8092 typedef struct log_find_operative_subtree_baton_t
8094 /* Mapping of const char * absolute working copy paths to those
8095 path's const char * repos absolute paths. */
8096 apr_hash_t *operative_children;
8098 /* As per the arguments of the same name to
8099 get_operative_immediate_children(). */
8100 const char *merge_source_fspath;
8101 const char *merge_target_abspath;
8103 svn_wc_context_t *wc_ctx;
8105 /* A pool to allocate additions to the hashes in. */
8106 apr_pool_t *result_pool;
8107 } log_find_operative_subtree_baton_t;
8109 /* A svn_log_entry_receiver_t callback for
8110 get_inoperative_immediate_children(). */
8111 static svn_error_t *
8112 log_find_operative_subtree_revs(void *baton,
8113 svn_log_entry_t *log_entry,
8116 log_find_operative_subtree_baton_t *log_baton = baton;
8117 apr_hash_index_t *hi;
8118 apr_pool_t *iterpool;
8120 /* It's possible that authz restrictions on the merge source prevent us
8121 from knowing about any of the changes for LOG_ENTRY->REVISION. */
8122 if (!log_entry->changed_paths2)
8123 return SVN_NO_ERROR;
8125 iterpool = svn_pool_create(pool);
8127 for (hi = apr_hash_first(pool, log_entry->changed_paths2);
8129 hi = apr_hash_next(hi))
8131 const char *path = apr_hash_this_key(hi);
8132 svn_log_changed_path2_t *change = apr_hash_this_val(hi);
8136 const char *potential_child;
8137 const char *rel_path =
8138 svn_fspath__skip_ancestor(log_baton->merge_source_fspath, path);
8140 /* Some affected paths might be the root of the merge source or
8141 entirely outside our subtree of interest. In either case they
8142 are not operative *immediate* children. */
8143 if (rel_path == NULL
8144 || rel_path[0] == '\0')
8147 svn_pool_clear(iterpool);
8149 child = svn_relpath_dirname(rel_path, iterpool);
8150 if (child[0] == '\0')
8152 /* The svn_log_changed_path2_t.node_kind members in
8153 LOG_ENTRY->CHANGED_PATHS2 may be set to
8154 svn_node_unknown, see svn_log_changed_path2_t and
8155 svn_fs_paths_changed2. In that case we check the
8156 type of the corresponding subtree in the merge
8158 svn_node_kind_t node_kind;
8160 if (change->node_kind == svn_node_unknown)
8162 const char *wc_child_abspath =
8163 svn_dirent_join(log_baton->merge_target_abspath,
8164 rel_path, iterpool);
8166 SVN_ERR(svn_wc_read_kind2(&node_kind, log_baton->wc_ctx,
8167 wc_child_abspath, FALSE, FALSE,
8172 node_kind = change->node_kind;
8175 /* We only care about immediate directory children if
8176 DEPTH is svn_depth_files. */
8177 if (log_baton->depth == svn_depth_files
8178 && node_kind != svn_node_dir)
8181 /* If depth is svn_depth_immediates, then we only care
8182 about changes to proper subtrees of PATH. If the change
8183 is to PATH itself then PATH is within the operational
8184 depth of the merge. */
8185 if (log_baton->depth == svn_depth_immediates)
8191 potential_child = svn_dirent_join(log_baton->merge_target_abspath,
8194 if (change->action == 'A'
8195 || !svn_hash_gets(log_baton->operative_children,
8198 svn_hash_sets(log_baton->operative_children,
8199 apr_pstrdup(log_baton->result_pool,
8201 apr_pstrdup(log_baton->result_pool, path));
8205 svn_pool_destroy(iterpool);
8206 return SVN_NO_ERROR;
8209 /* Find immediate subtrees of MERGE_TARGET_ABSPATH which would have
8210 additional differences applied if record_mergeinfo_for_dir_merge() were
8211 recording mergeinfo describing a merge at svn_depth_infinity, rather
8212 than at DEPTH (which is assumed to be shallow; if
8213 DEPTH == svn_depth_infinity then this function does nothing beyond
8214 setting *OPERATIVE_CHILDREN to an empty hash).
8216 MERGE_SOURCE_FSPATH is the absolute repository path of the merge
8217 source. OLDEST_REV and YOUNGEST_REV are the revisions merged from
8218 MERGE_SOURCE_FSPATH to MERGE_TARGET_ABSPATH.
8220 RA_SESSION points to MERGE_SOURCE_FSPATH.
8222 Set *OPERATIVE_CHILDREN to a hash (mapping const char * absolute
8223 working copy paths to those path's const char * repos absolute paths)
8224 containing all the immediate subtrees of MERGE_TARGET_ABSPATH which would
8225 have a different diff applied if MERGE_SOURCE_FSPATH
8226 -r(OLDEST_REV - 1):YOUNGEST_REV were merged to MERGE_TARGET_ABSPATH at
8227 svn_depth_infinity rather than DEPTH.
8229 RESULT_POOL is used to allocate the contents of *OPERATIVE_CHILDREN.
8230 SCRATCH_POOL is used for temporary allocations. */
8231 static svn_error_t *
8232 get_operative_immediate_children(apr_hash_t **operative_children,
8233 const char *merge_source_fspath,
8234 svn_revnum_t oldest_rev,
8235 svn_revnum_t youngest_rev,
8236 const char *merge_target_abspath,
8238 svn_wc_context_t *wc_ctx,
8239 svn_ra_session_t *ra_session,
8240 apr_pool_t *result_pool,
8241 apr_pool_t *scratch_pool)
8243 log_find_operative_subtree_baton_t log_baton;
8245 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
8246 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
8247 SVN_ERR_ASSERT(oldest_rev <= youngest_rev);
8249 *operative_children = apr_hash_make(result_pool);
8251 if (depth == svn_depth_infinity)
8252 return SVN_NO_ERROR;
8254 /* Now remove any paths from *OPERATIVE_CHILDREN that are inoperative when
8255 merging MERGE_SOURCE_REPOS_PATH -r(OLDEST_REV - 1):YOUNGEST_REV to
8256 MERGE_TARGET_ABSPATH at --depth infinity. */
8257 log_baton.operative_children = *operative_children;
8258 log_baton.merge_source_fspath = merge_source_fspath;
8259 log_baton.merge_target_abspath = merge_target_abspath;
8260 log_baton.depth = depth;
8261 log_baton.wc_ctx = wc_ctx;
8262 log_baton.result_pool = result_pool;
8264 SVN_ERR(get_log(ra_session, "", youngest_rev, oldest_rev,
8265 TRUE, /* discover_changed_paths */
8266 log_find_operative_subtree_revs,
8267 &log_baton, scratch_pool));
8269 return SVN_NO_ERROR;
8272 /* Helper for record_mergeinfo_for_dir_merge(): Identify which elements of
8273 CHILDREN_WITH_MERGEINFO need new mergeinfo set to accurately
8274 describe a merge, what inheritance type such new mergeinfo should have,
8275 and what subtrees can be ignored altogether.
8277 For each svn_client__merge_path_t CHILD in CHILDREN_WITH_MERGEINFO,
8278 set CHILD->RECORD_MERGEINFO and CHILD->RECORD_NONINHERITABLE to true
8279 if the subtree needs mergeinfo to describe the merge and if that
8280 mergeinfo should be non-inheritable respectively.
8282 If OPERATIVE_MERGE is true, then the merge being described is operative
8283 as per subtree_touched_by_merge(). OPERATIVE_MERGE is false otherwise.
8285 MERGED_RANGE, MERGEINFO_FSPATH, DEPTH, NOTIFY_B, and MERGE_B are all
8286 cascaded from record_mergeinfo_for_dir_merge's arguments of the same
8289 SCRATCH_POOL is used for temporary allocations.
8291 static svn_error_t *
8292 flag_subtrees_needing_mergeinfo(svn_boolean_t operative_merge,
8293 const svn_merge_range_t *merged_range,
8294 apr_array_header_t *children_with_mergeinfo,
8295 const char *mergeinfo_fspath,
8297 merge_cmd_baton_t *merge_b,
8298 apr_pool_t *scratch_pool)
8300 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
8302 apr_hash_t *operative_immediate_children = NULL;
8304 assert(! merge_b->dry_run);
8306 if (!merge_b->record_only
8307 && merged_range->start <= merged_range->end
8308 && (depth < svn_depth_infinity))
8309 SVN_ERR(get_operative_immediate_children(
8310 &operative_immediate_children,
8311 mergeinfo_fspath, merged_range->start + 1, merged_range->end,
8312 merge_b->target->abspath, depth, merge_b->ctx->wc_ctx,
8313 merge_b->ra_session1, scratch_pool, iterpool));
8315 /* Issue #4056: Walk NOTIFY_B->CHILDREN_WITH_MERGEINFO reverse depth-first
8316 order. This way each child knows if it has operative missing/switched
8317 children which necessitates non-inheritable mergeinfo. */
8318 for (i = children_with_mergeinfo->nelts - 1; i >= 0; i--)
8320 svn_client__merge_path_t *child =
8321 APR_ARRAY_IDX(children_with_mergeinfo, i,
8322 svn_client__merge_path_t *);
8324 /* Can't record mergeinfo on something that isn't here. */
8328 /* Verify that remove_children_with_deleted_mergeinfo() did its job */
8330 ||! merge_b->paths_with_deleted_mergeinfo
8331 || !svn_hash_gets(merge_b->paths_with_deleted_mergeinfo,
8334 /* Don't record mergeinfo on skipped paths. */
8335 if (svn_hash_gets(merge_b->skipped_abspaths, child->abspath))
8338 /* ### ptb: Yes, we could combine the following into a single
8339 ### conditional, but clarity would suffer (even more than
8340 ### it does now). */
8343 /* Always record mergeinfo on the merge target. */
8344 child->record_mergeinfo = TRUE;
8346 else if (merge_b->record_only && !merge_b->reintegrate_merge)
8348 /* Always record mergeinfo for --record-only merges. */
8349 child->record_mergeinfo = TRUE;
8351 else if (child->immediate_child_dir
8352 && !child->pre_merge_mergeinfo
8353 && operative_immediate_children
8354 && svn_hash_gets(operative_immediate_children, child->abspath))
8356 /* We must record mergeinfo on those issue #3642 children
8357 that are operative at a greater depth. */
8358 child->record_mergeinfo = TRUE;
8362 && subtree_touched_by_merge(child->abspath, merge_b, iterpool))
8364 svn_pool_clear(iterpool);
8366 /* This subtree was affected by the merge. */
8367 child->record_mergeinfo = TRUE;
8369 /* Were any CHILD's missing children skipped by the merge?
8370 If not, then CHILD's missing children don't need to be
8371 considered when recording mergeinfo describing the merge. */
8372 if (! merge_b->reintegrate_merge
8373 && child->missing_child
8374 && !path_is_subtree(child->abspath,
8375 merge_b->skipped_abspaths,
8378 child->missing_child = FALSE;
8381 /* If CHILD has an immediate switched child or children and
8382 none of these were touched by the merge, then we don't need
8383 need to do any special handling of those switched subtrees
8384 (e.g. record non-inheritable mergeinfo) when recording
8385 mergeinfo describing the merge. */
8386 if (child->switched_child)
8389 svn_boolean_t operative_switched_child = FALSE;
8392 j < children_with_mergeinfo->nelts;
8395 svn_client__merge_path_t *potential_child =
8396 APR_ARRAY_IDX(children_with_mergeinfo, j,
8397 svn_client__merge_path_t *);
8398 if (!svn_dirent_is_ancestor(child->abspath,
8399 potential_child->abspath))
8402 /* POTENTIAL_CHILD is a subtree of CHILD, but is it
8403 an immediate child? */
8404 if (strcmp(child->abspath,
8405 svn_dirent_dirname(potential_child->abspath,
8409 if (potential_child->switched
8410 && potential_child->record_mergeinfo)
8412 operative_switched_child = TRUE;
8417 /* Can we treat CHILD as if it has no switched children? */
8418 if (! operative_switched_child)
8419 child->switched_child = FALSE;
8423 if (child->record_mergeinfo)
8425 /* We need to record mergeinfo, but should that mergeinfo be
8427 svn_node_kind_t path_kind;
8428 SVN_ERR(svn_wc_read_kind2(&path_kind, merge_b->ctx->wc_ctx,
8429 child->abspath, FALSE, FALSE, iterpool));
8431 /* Only directories can have non-inheritable mergeinfo. */
8432 if (path_kind == svn_node_dir)
8434 /* There are two general cases where non-inheritable mergeinfo
8437 1) There merge target has missing subtrees (due to authz
8438 restrictions, switched subtrees, or a shallow working
8441 2) The operational depth of the merge itself is shallow. */
8443 /* We've already determined the first case. */
8444 child->record_noninheritable =
8445 child->missing_child || child->switched_child;
8447 /* The second case requires a bit more work. */
8450 /* If CHILD is the root of the merge target and the
8451 operational depth is empty or files, then the mere
8452 existence of operative immediate children means we
8453 must record non-inheritable mergeinfo.
8455 ### What about svn_depth_immediates? In that case
8456 ### the merge target needs only normal inheritable
8457 ### mergeinfo and the target's immediate children will
8458 ### get non-inheritable mergeinfo, assuming they
8459 ### need even that. */
8460 if (depth < svn_depth_immediates
8461 && operative_immediate_children
8462 && apr_hash_count(operative_immediate_children))
8463 child->record_noninheritable = TRUE;
8465 else if (depth == svn_depth_immediates)
8467 /* An immediate directory child of the merge target, which
8468 was affected by a --depth=immediates merge, needs
8469 non-inheritable mergeinfo. */
8470 if (svn_hash_gets(operative_immediate_children,
8472 child->record_noninheritable = TRUE;
8476 else /* child->record_mergeinfo */
8478 /* If CHILD is in NOTIFY_B->CHILDREN_WITH_MERGEINFO simply
8479 because it had no explicit mergeinfo of its own at the
8480 start of the merge but is the child of of some path with
8481 non-inheritable mergeinfo, then the explicit mergeinfo it
8482 has *now* was set by get_mergeinfo_paths() -- see criteria
8483 3 in that function's doc string. So since CHILD->ABSPATH
8484 was not touched by the merge we can remove the
8486 if (child->child_of_noninheritable)
8487 SVN_ERR(svn_client__record_wc_mergeinfo(child->abspath,
8494 svn_pool_destroy(iterpool);
8495 return SVN_NO_ERROR;
8498 /* Helper for do_directory_merge().
8500 If RESULT_CATALOG is NULL then record mergeinfo describing a merge of
8501 MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
8502 MERGEINFO_FSPATH to the merge target (and possibly its subtrees) described
8503 by NOTIFY_B->CHILDREN_WITH_MERGEINFO -- see the global comment
8504 'THE CHILDREN_WITH_MERGEINFO ARRAY'. Obviously this should only
8505 be called if recording mergeinfo -- see doc string for RECORD_MERGEINFO().
8507 If RESULT_CATALOG is not NULL, then don't record the new mergeinfo on the
8508 WC, but instead record it in RESULT_CATALOG, where the keys are absolute
8509 working copy paths and the values are the new mergeinfos for each.
8510 Allocate additions to RESULT_CATALOG in pool which RESULT_CATALOG was
8513 DEPTH, NOTIFY_B, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS are all
8514 cascaded from do_directory_merge's arguments of the same names.
8516 SCRATCH_POOL is used for temporary allocations.
8518 static svn_error_t *
8519 record_mergeinfo_for_dir_merge(svn_mergeinfo_catalog_t result_catalog,
8520 const svn_merge_range_t *merged_range,
8521 const char *mergeinfo_fspath,
8522 apr_array_header_t *children_with_mergeinfo,
8524 svn_boolean_t squelch_mergeinfo_notifications,
8525 merge_cmd_baton_t *merge_b,
8526 apr_pool_t *scratch_pool)
8529 svn_boolean_t is_rollback = (merged_range->start > merged_range->end);
8530 svn_boolean_t operative_merge;
8532 /* Update the WC mergeinfo here to account for our new
8533 merges, minus any unresolved conflicts and skips. */
8535 /* We need a scratch pool for iterations below. */
8536 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
8538 svn_merge_range_t range = *merged_range;
8540 assert(! merge_b->dry_run);
8542 /* Regardless of what subtrees in MERGE_B->target->abspath might be missing
8543 could this merge have been operative? */
8544 operative_merge = subtree_touched_by_merge(merge_b->target->abspath,
8547 /* If this couldn't be an operative merge then don't bother with
8548 the added complexity (and user confusion) of non-inheritable ranges.
8549 There is no harm in subtrees inheriting inoperative mergeinfo. */
8550 if (!operative_merge)
8551 range.inheritable = TRUE;
8553 /* Remove absent children at or under MERGE_B->target->abspath from
8554 NOTIFY_B->CHILDREN_WITH_MERGEINFO
8555 before we calculate the merges performed. */
8556 remove_absent_children(merge_b->target->abspath,
8557 children_with_mergeinfo);
8559 /* Determine which subtrees of interest need mergeinfo recorded... */
8560 SVN_ERR(flag_subtrees_needing_mergeinfo(operative_merge, &range,
8561 children_with_mergeinfo,
8562 mergeinfo_fspath, depth,
8563 merge_b, iterpool));
8565 /* ...and then record it. */
8566 for (i = 0; i < children_with_mergeinfo->nelts; i++)
8568 const char *child_repos_path;
8569 const char *child_merge_src_fspath;
8570 svn_rangelist_t *child_merge_rangelist;
8571 apr_hash_t *child_merges;
8572 svn_client__merge_path_t *child =
8573 APR_ARRAY_IDX(children_with_mergeinfo, i,
8574 svn_client__merge_path_t *);
8575 SVN_ERR_ASSERT(child);
8577 svn_pool_clear(iterpool);
8579 if (child->record_mergeinfo)
8581 child_repos_path = svn_dirent_skip_ancestor(merge_b->target->abspath,
8583 SVN_ERR_ASSERT(child_repos_path != NULL);
8584 child_merge_src_fspath = svn_fspath__join(mergeinfo_fspath,
8587 /* Filter any ranges from each child's natural history before
8588 setting mergeinfo describing the merge. */
8589 SVN_ERR(filter_natural_history_from_mergeinfo(
8590 &child_merge_rangelist, child_merge_src_fspath,
8591 child->implicit_mergeinfo, &range, iterpool));
8593 if (child_merge_rangelist->nelts == 0)
8596 if (!squelch_mergeinfo_notifications)
8598 /* If the merge source has a gap, then don't mention
8599 those gap revisions in the notification. */
8600 remove_source_gap(&range, merge_b->implicit_src_gap);
8601 notify_mergeinfo_recording(child->abspath, &range,
8602 merge_b->ctx, iterpool);
8605 /* If we are here we know we will be recording some mergeinfo, but
8606 before we do, set override mergeinfo on skipped paths so they
8607 don't incorrectly inherit the mergeinfo we are about to set. */
8609 SVN_ERR(record_skips_in_mergeinfo(mergeinfo_fspath,
8610 child_merge_rangelist,
8611 is_rollback, merge_b, iterpool));
8613 /* We may need to record non-inheritable mergeinfo that applies
8614 only to CHILD->ABSPATH. */
8615 if (child->record_noninheritable)
8616 svn_rangelist__set_inheritance(child_merge_rangelist, FALSE);
8618 /* If CHILD has inherited mergeinfo set it before
8619 recording the first merge range. */
8620 if (child->inherited_mergeinfo)
8621 SVN_ERR(svn_client__record_wc_mergeinfo(
8623 child->pre_merge_mergeinfo,
8624 FALSE, merge_b->ctx,
8626 if (merge_b->implicit_src_gap)
8628 /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
8629 so it will work with the svn_rangelist_remove API. */
8631 SVN_ERR(svn_rangelist_reverse(child_merge_rangelist,
8634 SVN_ERR(svn_rangelist_remove(&child_merge_rangelist,
8635 merge_b->implicit_src_gap,
8636 child_merge_rangelist, FALSE,
8639 SVN_ERR(svn_rangelist_reverse(child_merge_rangelist,
8643 child_merges = apr_hash_make(iterpool);
8647 If we are describing a forward merge, then the naive mergeinfo
8648 defined by MERGE_SOURCE_PATH:MERGED_RANGE->START:
8649 MERGE_SOURCE_PATH:MERGED_RANGE->END may contain non-existent
8650 path-revs or may describe other lines of history. We must
8651 remove these invalid portion(s) before recording mergeinfo
8652 describing the merge.
8656 If CHILD is the merge target we know that
8657 MERGE_SOURCE_PATH:MERGED_RANGE->END exists. Further, if there
8658 were no copies in MERGE_SOURCE_PATH's history going back to
8659 RANGE->START then we know that
8660 MERGE_SOURCE_PATH:MERGED_RANGE->START exists too and the two
8661 describe an unbroken line of history, and thus
8662 MERGE_SOURCE_PATH:MERGED_RANGE->START:
8663 MERGE_SOURCE_PATH:MERGED_RANGE->END is a valid description of
8664 the merge -- see normalize_merge_sources() and the global comment
8665 'MERGEINFO MERGE SOURCE NORMALIZATION'.
8667 However, if there *was* a copy, then
8668 MERGE_SOURCE_PATH:MERGED_RANGE->START doesn't exist or is
8669 unrelated to MERGE_SOURCE_PATH:MERGED_RANGE->END. Also, we
8670 don't know if (MERGE_SOURCE_PATH:MERGED_RANGE->START)+1 through
8671 (MERGE_SOURCE_PATH:MERGED_RANGE->END)-1 actually exist.
8673 If CHILD is a subtree of the merge target, then nothing is
8674 guaranteed beyond the fact that MERGE_SOURCE_PATH exists at
8675 MERGED_RANGE->END. */
8676 if ((!merge_b->record_only || merge_b->reintegrate_merge)
8680 svn_mergeinfo_t subtree_history_as_mergeinfo;
8681 svn_rangelist_t *child_merge_src_rangelist;
8682 svn_client__pathrev_t *subtree_mergeinfo_pathrev
8683 = svn_client__pathrev_create_with_relpath(
8684 merge_b->target->loc.repos_root_url,
8685 merge_b->target->loc.repos_uuid,
8686 merged_range->end, child_merge_src_fspath + 1,
8689 /* Confirm that the naive mergeinfo we want to set on
8690 CHILD->ABSPATH both exists and is part of
8691 (MERGE_SOURCE_PATH+CHILD_REPOS_PATH)@MERGED_RANGE->END's
8693 /* We know MERGED_RANGE->END is younger than MERGE_RANGE->START
8694 because we only do this for forward merges. */
8695 err = svn_client__get_history_as_mergeinfo(
8696 &subtree_history_as_mergeinfo, NULL,
8697 subtree_mergeinfo_pathrev,
8698 merged_range->end, merged_range->start,
8699 merge_b->ra_session2, merge_b->ctx, iterpool);
8701 /* If CHILD is a subtree it may have been deleted prior to
8702 MERGED_RANGE->END so the above call to get its history
8706 if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
8707 return svn_error_trace(err);
8708 svn_error_clear(err);
8712 child_merge_src_rangelist = svn_hash_gets(
8713 subtree_history_as_mergeinfo,
8714 child_merge_src_fspath);
8715 SVN_ERR(svn_rangelist_intersect(&child_merge_rangelist,
8716 child_merge_rangelist,
8717 child_merge_src_rangelist,
8719 if (child->record_noninheritable)
8720 svn_rangelist__set_inheritance(child_merge_rangelist,
8725 svn_hash_sets(child_merges, child->abspath, child_merge_rangelist);
8726 SVN_ERR(update_wc_mergeinfo(result_catalog,
8728 child_merge_src_fspath,
8729 child_merges, is_rollback,
8730 merge_b->ctx, iterpool));
8732 /* Once is enough: We don't need to record mergeinfo describing
8733 the merge a second. If CHILD->ABSPATH is in
8734 MERGE_B->ADDED_ABSPATHS, we'll do just that, so remove the
8735 former from the latter. */
8736 svn_hash_sets(merge_b->added_abspaths, child->abspath, NULL);
8739 /* Elide explicit subtree mergeinfo whether or not we updated it. */
8742 svn_boolean_t in_switched_subtree = FALSE;
8744 if (child->switched)
8745 in_switched_subtree = TRUE;
8748 /* Check if CHILD is part of a switched subtree */
8749 svn_client__merge_path_t *parent;
8753 parent = APR_ARRAY_IDX(children_with_mergeinfo,
8754 j, svn_client__merge_path_t *);
8757 && svn_dirent_is_ancestor(parent->abspath,
8760 in_switched_subtree = TRUE;
8766 /* Allow mergeinfo on switched subtrees to elide to the
8767 repository. Otherwise limit elision to the merge target
8768 for now. do_merge() will eventually try to
8769 elide that when the merge is complete. */
8770 SVN_ERR(svn_client__elide_mergeinfo(
8772 in_switched_subtree ? NULL : merge_b->target->abspath,
8773 merge_b->ctx, iterpool));
8775 } /* (i = 0; i < notify_b->children_with_mergeinfo->nelts; i++) */
8777 svn_pool_destroy(iterpool);
8778 return SVN_NO_ERROR;
8781 /* Helper for do_directory_merge().
8783 Record mergeinfo describing a merge of
8784 MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
8785 MERGEINFO_FSPATH to each path in ADDED_ABSPATHS which has explicit
8786 mergeinfo or is the immediate child of a parent with explicit
8787 non-inheritable mergeinfo.
8789 DEPTH, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS, are
8790 cascaded from do_directory_merge's arguments of the same names.
8792 Note: This is intended to support forward merges only, i.e.
8793 MERGED_RANGE->START must be older than MERGED_RANGE->END.
8795 static svn_error_t *
8796 record_mergeinfo_for_added_subtrees(
8797 svn_merge_range_t *merged_range,
8798 const char *mergeinfo_fspath,
8800 svn_boolean_t squelch_mergeinfo_notifications,
8801 apr_hash_t *added_abspaths,
8802 merge_cmd_baton_t *merge_b,
8805 apr_pool_t *iterpool;
8806 apr_hash_index_t *hi;
8808 /* If no paths were added by the merge then we have nothing to do. */
8809 if (!added_abspaths)
8810 return SVN_NO_ERROR;
8812 SVN_ERR_ASSERT(merged_range->start < merged_range->end);
8814 iterpool = svn_pool_create(pool);
8815 for (hi = apr_hash_first(pool, added_abspaths); hi; hi = apr_hash_next(hi))
8817 const char *added_abspath = apr_hash_this_key(hi);
8818 const char *dir_abspath;
8819 svn_mergeinfo_t parent_mergeinfo;
8820 svn_mergeinfo_t added_path_mergeinfo;
8822 svn_pool_clear(iterpool);
8823 dir_abspath = svn_dirent_dirname(added_abspath, iterpool);
8825 /* Grab the added path's explicit mergeinfo. */
8826 SVN_ERR(svn_client__get_wc_mergeinfo(&added_path_mergeinfo, NULL,
8827 svn_mergeinfo_explicit,
8828 added_abspath, NULL, NULL, FALSE,
8829 merge_b->ctx, iterpool, iterpool));
8831 /* If the added path doesn't have explicit mergeinfo, does its immediate
8832 parent have non-inheritable mergeinfo? */
8833 if (!added_path_mergeinfo)
8834 SVN_ERR(svn_client__get_wc_mergeinfo(&parent_mergeinfo, NULL,
8835 svn_mergeinfo_explicit,
8836 dir_abspath, NULL, NULL, FALSE,
8838 iterpool, iterpool));
8840 if (added_path_mergeinfo
8841 || svn_mergeinfo__is_noninheritable(parent_mergeinfo, iterpool))
8843 svn_node_kind_t added_path_kind;
8844 svn_mergeinfo_t merge_mergeinfo;
8845 svn_mergeinfo_t adds_history_as_mergeinfo;
8846 svn_rangelist_t *rangelist;
8847 const char *rel_added_path;
8848 const char *added_path_mergeinfo_fspath;
8849 svn_client__pathrev_t *added_path_pathrev;
8851 SVN_ERR(svn_wc_read_kind2(&added_path_kind, merge_b->ctx->wc_ctx,
8852 added_abspath, FALSE, FALSE, iterpool));
8854 /* Calculate the naive mergeinfo describing the merge. */
8855 merge_mergeinfo = apr_hash_make(iterpool);
8856 rangelist = svn_rangelist__initialize(
8857 merged_range->start, merged_range->end,
8858 ((added_path_kind == svn_node_file)
8859 || (!(depth == svn_depth_infinity
8860 || depth == svn_depth_immediates))),
8863 /* Create the new mergeinfo path for added_path's mergeinfo.
8864 (added_abspath had better be a child of MERGE_B->target->abspath
8865 or something is *really* wrong.) */
8866 rel_added_path = svn_dirent_is_child(merge_b->target->abspath,
8867 added_abspath, iterpool);
8868 SVN_ERR_ASSERT(rel_added_path);
8869 added_path_mergeinfo_fspath = svn_fspath__join(mergeinfo_fspath,
8872 svn_hash_sets(merge_mergeinfo, added_path_mergeinfo_fspath,
8875 /* Don't add new mergeinfo to describe the merge if that mergeinfo
8876 contains non-existent merge sources.
8878 We know that MERGEINFO_PATH/rel_added_path's history does not
8879 span MERGED_RANGE->START:MERGED_RANGE->END but rather that it
8880 was added at some revions greater than MERGED_RANGE->START
8881 (assuming this is a forward merge). It may have been added,
8882 deleted, and re-added many times. The point is that we cannot
8883 blindly apply the naive mergeinfo calculated above because it
8884 will describe non-existent merge sources. To avoid this we get
8885 take the intersection of the naive mergeinfo with
8886 MERGEINFO_PATH/rel_added_path's history. */
8887 added_path_pathrev = svn_client__pathrev_create_with_relpath(
8888 merge_b->target->loc.repos_root_url,
8889 merge_b->target->loc.repos_uuid,
8890 MAX(merged_range->start, merged_range->end),
8891 added_path_mergeinfo_fspath + 1, iterpool);
8892 SVN_ERR(svn_client__get_history_as_mergeinfo(
8893 &adds_history_as_mergeinfo, NULL,
8895 MAX(merged_range->start, merged_range->end),
8896 MIN(merged_range->start, merged_range->end),
8897 merge_b->ra_session2, merge_b->ctx, iterpool));
8899 SVN_ERR(svn_mergeinfo_intersect2(&merge_mergeinfo,
8901 adds_history_as_mergeinfo,
8902 FALSE, iterpool, iterpool));
8904 /* Combine the explicit mergeinfo on the added path (if any)
8905 with the mergeinfo describing this merge. */
8906 if (added_path_mergeinfo)
8907 SVN_ERR(svn_mergeinfo_merge2(merge_mergeinfo,
8908 added_path_mergeinfo,
8909 iterpool, iterpool));
8910 SVN_ERR(svn_client__record_wc_mergeinfo(
8911 added_abspath, merge_mergeinfo,
8912 !squelch_mergeinfo_notifications, merge_b->ctx, iterpool));
8915 svn_pool_destroy(iterpool);
8917 return SVN_NO_ERROR;
8919 /* Baton structure for log_noop_revs. */
8920 typedef struct log_noop_baton_t
8922 /* See the comment 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start
8924 apr_array_header_t *children_with_mergeinfo;
8926 /* Absolute repository path of younger of the two merge sources
8928 const char *source_fspath;
8930 /* The merge target. */
8931 const merge_target_t *target;
8933 /* Initially empty rangelists allocated in POOL. The rangelists are
8934 * populated across multiple invocations of log_noop_revs(). */
8935 svn_rangelist_t *operative_ranges;
8936 svn_rangelist_t *merged_ranges;
8938 /* Pool to store the rangelists. */
8942 /* Helper for log_noop_revs: Merge a svn_merge_range_t representation of
8943 REVISION into RANGELIST. New elements added to rangelist are allocated
8946 This is *not* a general purpose rangelist merge but a special replacement
8947 for svn_rangelist_merge when REVISION is guaranteed to be younger than any
8948 element in RANGELIST. svn_rangelist_merge is O(n) worst-case (i.e. when
8949 all the ranges in output rangelist are older than the incoming changes).
8950 This turns the special case of a single incoming younger range into O(1).
8952 static svn_error_t *
8953 rangelist_merge_revision(svn_rangelist_t *rangelist,
8954 svn_revnum_t revision,
8955 apr_pool_t *result_pool)
8957 svn_merge_range_t *new_range;
8958 if (rangelist->nelts)
8960 svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
8961 svn_merge_range_t *);
8962 if (range->end == revision - 1)
8964 /* REVISION is adjacent to the youngest range in RANGELIST
8965 so we can simply expand that range to encompass REVISION. */
8966 range->end = revision;
8967 return SVN_NO_ERROR;
8970 new_range = apr_palloc(result_pool, sizeof(*new_range));
8971 new_range->start = revision - 1;
8972 new_range->end = revision;
8973 new_range->inheritable = TRUE;
8975 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = new_range;
8977 return SVN_NO_ERROR;
8980 /* Implements the svn_log_entry_receiver_t interface.
8982 BATON is an log_noop_baton_t *.
8984 Add LOG_ENTRY->REVISION to BATON->OPERATIVE_RANGES.
8986 If LOG_ENTRY->REVISION has already been fully merged to
8987 BATON->target->abspath per the mergeinfo in BATON->CHILDREN_WITH_MERGEINFO,
8988 then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES.
8990 Use SCRATCH_POOL for temporary allocations. Allocate additions to
8991 BATON->MERGED_RANGES and BATON->OPERATIVE_RANGES in BATON->POOL.
8993 Note: This callback must be invoked from oldest LOG_ENTRY->REVISION
8994 to youngest LOG_ENTRY->REVISION -- see rangelist_merge_revision().
8996 static svn_error_t *
8997 log_noop_revs(void *baton,
8998 svn_log_entry_t *log_entry,
8999 apr_pool_t *scratch_pool)
9001 log_noop_baton_t *log_gap_baton = baton;
9002 apr_hash_index_t *hi;
9003 svn_revnum_t revision;
9004 svn_boolean_t log_entry_rev_required = FALSE;
9006 revision = log_entry->revision;
9008 /* It's possible that authz restrictions on the merge source prevent us
9009 from knowing about any of the changes for LOG_ENTRY->REVISION. */
9010 if (!log_entry->changed_paths2)
9011 return SVN_NO_ERROR;
9013 /* Unconditionally add LOG_ENTRY->REVISION to BATON->OPERATIVE_MERGES. */
9014 SVN_ERR(rangelist_merge_revision(log_gap_baton->operative_ranges,
9016 log_gap_baton->pool));
9018 /* Examine each path affected by LOG_ENTRY->REVISION. If the explicit or
9019 inherited mergeinfo for *all* of the corresponding paths under
9020 BATON->target->abspath reflects that LOG_ENTRY->REVISION has been
9021 merged, then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES. */
9022 for (hi = apr_hash_first(scratch_pool, log_entry->changed_paths2);
9024 hi = apr_hash_next(hi))
9026 const char *fspath = apr_hash_this_key(hi);
9027 const char *rel_path;
9028 const char *cwmi_abspath;
9029 svn_rangelist_t *paths_explicit_rangelist = NULL;
9030 svn_boolean_t mergeinfo_inherited = FALSE;
9032 /* Adjust REL_PATH so it is relative to the merge source then use it to
9033 calculate what path in the merge target would be affected by this
9035 rel_path = svn_fspath__skip_ancestor(log_gap_baton->source_fspath,
9037 /* Is PATH even within the merge target? If it isn't we
9038 can disregard it altogether. */
9039 if (rel_path == NULL)
9041 cwmi_abspath = svn_dirent_join(log_gap_baton->target->abspath,
9042 rel_path, scratch_pool);
9044 /* Find any explicit or inherited mergeinfo for PATH. */
9045 while (!log_entry_rev_required)
9047 svn_client__merge_path_t *child = get_child_with_mergeinfo(
9048 log_gap_baton->children_with_mergeinfo, cwmi_abspath);
9050 if (child && child->pre_merge_mergeinfo)
9052 /* Found some explicit mergeinfo, grab any ranges
9054 paths_explicit_rangelist =
9055 svn_hash_gets(child->pre_merge_mergeinfo, fspath);
9059 if (cwmi_abspath[0] == '\0'
9060 || svn_dirent_is_root(cwmi_abspath, strlen(cwmi_abspath))
9061 || strcmp(log_gap_baton->target->abspath, cwmi_abspath) == 0)
9063 /* Can't crawl any higher. */
9067 /* Didn't find anything so crawl up to the parent. */
9068 cwmi_abspath = svn_dirent_dirname(cwmi_abspath, scratch_pool);
9069 fspath = svn_fspath__dirname(fspath, scratch_pool);
9071 /* At this point *if* we find mergeinfo it will be inherited. */
9072 mergeinfo_inherited = TRUE;
9075 if (paths_explicit_rangelist)
9077 svn_rangelist_t *intersecting_range;
9078 svn_rangelist_t *rangelist;
9080 rangelist = svn_rangelist__initialize(revision - 1, revision, TRUE,
9083 /* If PATH inherited mergeinfo we must consider inheritance in the
9084 event the inherited mergeinfo is actually non-inheritable. */
9085 SVN_ERR(svn_rangelist_intersect(&intersecting_range,
9086 paths_explicit_rangelist,
9088 mergeinfo_inherited, scratch_pool));
9090 if (intersecting_range->nelts == 0)
9091 log_entry_rev_required = TRUE;
9095 log_entry_rev_required = TRUE;
9099 if (!log_entry_rev_required)
9100 SVN_ERR(rangelist_merge_revision(log_gap_baton->merged_ranges,
9102 log_gap_baton->pool));
9104 return SVN_NO_ERROR;
9107 /* Helper for do_directory_merge().
9109 SOURCE is cascaded from the argument of the same name in
9110 do_directory_merge(). TARGET is the merge target. RA_SESSION is the
9111 session for SOURCE->loc2.
9113 Find all the ranges required by subtrees in
9114 CHILDREN_WITH_MERGEINFO that are *not* required by
9115 TARGET->abspath (i.e. CHILDREN_WITH_MERGEINFO[0]). If such
9116 ranges exist, then find any subset of ranges which, if merged, would be
9117 inoperative. Finally, if any inoperative ranges are found then remove
9118 these ranges from all of the subtree's REMAINING_RANGES.
9120 This function should only be called when honoring mergeinfo during
9121 forward merges (i.e. SOURCE->rev1 < SOURCE->rev2).
9123 static svn_error_t *
9124 remove_noop_subtree_ranges(const merge_source_t *source,
9125 const merge_target_t *target,
9126 svn_ra_session_t *ra_session,
9127 apr_array_header_t *children_with_mergeinfo,
9128 apr_pool_t *result_pool,
9129 apr_pool_t *scratch_pool)
9131 /* ### Do we need to check that we are at a uniform working revision? */
9133 svn_client__merge_path_t *root_child =
9134 APR_ARRAY_IDX(children_with_mergeinfo, 0, svn_client__merge_path_t *);
9135 svn_rangelist_t *requested_ranges;
9136 svn_rangelist_t *subtree_gap_ranges;
9137 svn_rangelist_t *subtree_remaining_ranges;
9138 log_noop_baton_t log_gap_baton;
9139 svn_merge_range_t *oldest_gap_rev;
9140 svn_merge_range_t *youngest_gap_rev;
9141 svn_rangelist_t *inoperative_ranges;
9142 apr_pool_t *iterpool;
9143 const char *longest_common_subtree_ancestor = NULL;
9146 assert(session_url_is(ra_session, source->loc2->url, scratch_pool));
9148 /* This function is only intended to work with forward merges. */
9149 if (source->loc1->rev > source->loc2->rev)
9150 return SVN_NO_ERROR;
9152 /* Another easy out: There are no subtrees. */
9153 if (children_with_mergeinfo->nelts < 2)
9154 return SVN_NO_ERROR;
9156 subtree_remaining_ranges = apr_array_make(scratch_pool, 1,
9157 sizeof(svn_merge_range_t *));
9159 /* Given the requested merge of SOURCE->rev1:rev2 might there be any
9160 part of this range required for subtrees but not for the target? */
9161 requested_ranges = svn_rangelist__initialize(MIN(source->loc1->rev,
9163 MAX(source->loc1->rev,
9165 TRUE, scratch_pool);
9166 SVN_ERR(svn_rangelist_remove(&subtree_gap_ranges,
9167 root_child->remaining_ranges,
9168 requested_ranges, FALSE, scratch_pool));
9170 /* Early out, nothing to operate on */
9171 if (!subtree_gap_ranges->nelts)
9172 return SVN_NO_ERROR;
9174 /* Create a rangelist describing every range required across all subtrees. */
9175 iterpool = svn_pool_create(scratch_pool);
9176 for (i = 1; i < children_with_mergeinfo->nelts; i++)
9178 svn_client__merge_path_t *child =
9179 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
9181 svn_pool_clear(iterpool);
9183 /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
9184 if (child->remaining_ranges && child->remaining_ranges->nelts)
9186 /* Issue #4269: Keep track of the longest common ancestor of all the
9187 subtrees which require merges. This may be a child of
9188 TARGET->ABSPATH, which will allow us to narrow the log request
9190 if (longest_common_subtree_ancestor)
9191 longest_common_subtree_ancestor = svn_dirent_get_longest_ancestor(
9192 longest_common_subtree_ancestor, child->abspath, scratch_pool);
9194 longest_common_subtree_ancestor = child->abspath;
9196 SVN_ERR(svn_rangelist_merge2(subtree_remaining_ranges,
9197 child->remaining_ranges,
9198 scratch_pool, iterpool));
9201 svn_pool_destroy(iterpool);
9203 /* It's possible that none of the subtrees had any remaining ranges. */
9204 if (!subtree_remaining_ranges->nelts)
9205 return SVN_NO_ERROR;
9207 /* Ok, *finally* we can answer what part(s) of SOURCE->rev1:rev2 are
9208 required for the subtrees but not the target. */
9209 SVN_ERR(svn_rangelist_intersect(&subtree_gap_ranges,
9211 subtree_remaining_ranges, FALSE,
9214 /* Another early out */
9215 if (!subtree_gap_ranges->nelts)
9216 return SVN_NO_ERROR;
9218 /* One or more subtrees need some revisions that the target doesn't need.
9219 Use log to determine if any of these revisions are inoperative. */
9220 oldest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges, 0, svn_merge_range_t *);
9221 youngest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges,
9222 subtree_gap_ranges->nelts - 1, svn_merge_range_t *);
9224 /* Set up the log baton. */
9225 log_gap_baton.children_with_mergeinfo = children_with_mergeinfo;
9226 log_gap_baton.source_fspath
9227 = svn_client__pathrev_fspath(source->loc2, result_pool);
9228 log_gap_baton.target = target;
9229 log_gap_baton.merged_ranges = apr_array_make(scratch_pool, 0,
9230 sizeof(svn_revnum_t *));
9231 log_gap_baton.operative_ranges = apr_array_make(scratch_pool, 0,
9232 sizeof(svn_revnum_t *));
9233 log_gap_baton.pool = svn_pool_create(scratch_pool);
9235 /* Find the longest common ancestor of all subtrees relative to
9236 RA_SESSION's URL. */
9237 if (longest_common_subtree_ancestor)
9238 longest_common_subtree_ancestor =
9239 svn_dirent_skip_ancestor(target->abspath,
9240 longest_common_subtree_ancestor);
9242 longest_common_subtree_ancestor = "";
9244 /* Invoke the svn_log_entry_receiver_t receiver log_noop_revs() from
9245 oldest to youngest. The receiver is optimized to add ranges to
9246 log_gap_baton.merged_ranges and log_gap_baton.operative_ranges, but
9247 requires that the revs arrive oldest to youngest -- see log_noop_revs()
9248 and rangelist_merge_revision(). */
9249 err = get_log(ra_session, longest_common_subtree_ancestor,
9250 oldest_gap_rev->start + 1, youngest_gap_rev->end, TRUE,
9251 log_noop_revs, &log_gap_baton, scratch_pool);
9253 /* It's possible that the only subtrees with mergeinfo in TARGET don't have
9254 any corresponding subtree in SOURCE between SOURCE->REV1 < SOURCE->REV2.
9255 So it's also possible that we may ask for the logs of non-existent paths.
9256 If we do, then assume that no subtree requires any ranges that are not
9257 already required by the TARGET. */
9260 if (err->apr_err != SVN_ERR_FS_NOT_FOUND
9261 && longest_common_subtree_ancestor[0] != '\0')
9262 return svn_error_trace(err);
9264 /* Asked about a non-existent subtree in SOURCE. */
9265 svn_error_clear(err);
9266 log_gap_baton.merged_ranges =
9267 svn_rangelist__initialize(oldest_gap_rev->start,
9268 youngest_gap_rev->end,
9269 TRUE, scratch_pool);
9273 inoperative_ranges = svn_rangelist__initialize(oldest_gap_rev->start,
9274 youngest_gap_rev->end,
9275 TRUE, scratch_pool);
9276 SVN_ERR(svn_rangelist_remove(&(inoperative_ranges),
9277 log_gap_baton.operative_ranges,
9278 inoperative_ranges, FALSE, scratch_pool));
9279 SVN_ERR(svn_rangelist_merge2(log_gap_baton.merged_ranges, inoperative_ranges,
9280 scratch_pool, scratch_pool));
9283 for (i = 1; i < children_with_mergeinfo->nelts; i++)
9285 svn_client__merge_path_t *child =
9286 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
9288 /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
9289 if (child->remaining_ranges && child->remaining_ranges->nelts)
9291 /* Remove inoperative ranges from all children so we don't perform
9292 inoperative editor drives. */
9293 SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
9294 log_gap_baton.merged_ranges,
9295 child->remaining_ranges,
9296 FALSE, result_pool));
9300 svn_pool_destroy(log_gap_baton.pool);
9302 return SVN_NO_ERROR;
9305 /* Perform a merge of changes in SOURCE to the working copy path
9306 TARGET_ABSPATH. Both URLs in SOURCE, and TARGET_ABSPATH all represent
9307 directories -- for the single file case, the caller should use
9310 CHILDREN_WITH_MERGEINFO and MERGE_B describe the merge being performed
9311 As this function is for a mergeinfo-aware merge, SOURCE->ancestral
9312 should be TRUE, and SOURCE->loc1 must be a historical ancestor of
9313 SOURCE->loc2, or vice-versa (see `MERGEINFO MERGE SOURCE NORMALIZATION'
9314 for more requirements around SOURCE).
9316 Mergeinfo changes will be recorded unless MERGE_B->dry_run is true.
9318 If mergeinfo is being recorded, SQUELCH_MERGEINFO_NOTIFICATIONS is FALSE,
9319 and MERGE_B->CTX->NOTIFY_FUNC2 is not NULL, then call
9320 MERGE_B->CTX->NOTIFY_FUNC2 with MERGE_B->CTX->NOTIFY_BATON2 and a
9321 svn_wc_notify_merge_record_info_begin notification before any mergeinfo
9322 changes are made to describe the merge performed.
9324 If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
9325 is not NULL, then don't record the new mergeinfo on the WC, but instead
9326 record it in RESULT_CATALOG, where the keys are absolute working copy
9327 paths and the values are the new mergeinfos for each. Allocate additions
9328 to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
9330 Handle DEPTH as documented for svn_client_merge5().
9332 CONFLICT_REPORT is as documented for do_directory_merge().
9334 Perform any temporary allocations in SCRATCH_POOL.
9336 NOTE: This is a wrapper around drive_merge_report_editor() which
9337 handles the complexities inherent to situations where a given
9338 directory's children may have intersecting merges (because they
9339 meet one or more of the criteria described in get_mergeinfo_paths()).
9341 static svn_error_t *
9342 do_mergeinfo_aware_dir_merge(svn_mergeinfo_catalog_t result_catalog,
9343 single_range_conflict_report_t **conflict_report,
9344 const merge_source_t *source,
9345 const char *target_abspath,
9346 apr_array_header_t *children_with_mergeinfo,
9347 const svn_diff_tree_processor_t *processor,
9349 svn_boolean_t squelch_mergeinfo_notifications,
9350 merge_cmd_baton_t *merge_b,
9351 apr_pool_t *result_pool,
9352 apr_pool_t *scratch_pool)
9354 /* The range defining the mergeinfo we will record to describe the merge
9355 (assuming we are recording mergeinfo
9357 Note: This may be a subset of SOURCE->rev1:rev2 if
9358 populate_remaining_ranges() determines that some part of
9359 SOURCE->rev1:rev2 has already been wholly merged to TARGET_ABSPATH.
9360 Also, the actual editor drive(s) may be a subset of RANGE, if
9361 remove_noop_subtree_ranges() and/or fix_deleted_subtree_ranges()
9362 further tweak things. */
9363 svn_merge_range_t range;
9365 svn_ra_session_t *ra_session;
9366 svn_client__merge_path_t *target_merge_path;
9367 svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
9369 SVN_ERR_ASSERT(source->ancestral);
9371 /*** If we get here, we're dealing with related sources from the
9372 same repository as the target -- merge tracking might be
9375 *conflict_report = NULL;
9377 /* Point our RA_SESSION to the URL of our youngest merge source side. */
9378 ra_session = is_rollback ? merge_b->ra_session1 : merge_b->ra_session2;
9380 /* Fill NOTIFY_B->CHILDREN_WITH_MERGEINFO with child paths (const
9381 svn_client__merge_path_t *) which might have intersecting merges
9382 because they meet one or more of the criteria described in
9383 get_mergeinfo_paths(). Here the paths are arranged in a depth
9385 SVN_ERR(get_mergeinfo_paths(children_with_mergeinfo,
9386 merge_b->target, depth,
9387 merge_b->dry_run, merge_b->same_repos,
9388 merge_b->ctx, scratch_pool, scratch_pool));
9390 /* The first item from the NOTIFY_B->CHILDREN_WITH_MERGEINFO is always
9391 the target thanks to depth-first ordering. */
9392 target_merge_path = APR_ARRAY_IDX(children_with_mergeinfo, 0,
9393 svn_client__merge_path_t *);
9395 /* If we are honoring mergeinfo, then for each item in
9396 NOTIFY_B->CHILDREN_WITH_MERGEINFO, we need to calculate what needs to be
9397 merged, and then merge it. Otherwise, we just merge what we were asked
9398 to merge across the whole tree. */
9399 SVN_ERR(populate_remaining_ranges(children_with_mergeinfo,
9401 merge_b, scratch_pool, scratch_pool));
9403 /* Always start with a range which describes the most inclusive merge
9404 possible, i.e. SOURCE->rev1:rev2. */
9405 range.start = source->loc1->rev;
9406 range.end = source->loc2->rev;
9407 range.inheritable = TRUE;
9409 if (!merge_b->reintegrate_merge)
9411 svn_revnum_t new_range_start, start_rev;
9412 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
9414 /* The merge target TARGET_ABSPATH and/or its subtrees may not need all
9415 of SOURCE->rev1:rev2 applied. So examine
9416 NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the oldest starting
9417 revision that actually needs to be merged (for reverse merges this is
9418 the youngest starting revision).
9420 We'll do this twice, right now for the start of the mergeinfo we will
9421 ultimately record to describe this merge and then later for the
9422 start of the actual editor drive. */
9423 new_range_start = get_most_inclusive_rev(
9424 children_with_mergeinfo, is_rollback, TRUE);
9425 if (SVN_IS_VALID_REVNUM(new_range_start))
9426 range.start = new_range_start;
9428 /* Remove inoperative ranges from any subtrees' remaining_ranges
9429 to spare the expense of noop editor drives. */
9431 SVN_ERR(remove_noop_subtree_ranges(source, merge_b->target,
9433 children_with_mergeinfo,
9434 scratch_pool, iterpool));
9436 /* Adjust subtrees' remaining_ranges to deal with issue #3067:
9437 * "subtrees that don't exist at the start or end of a merge range
9438 * shouldn't break the merge". */
9439 SVN_ERR(fix_deleted_subtree_ranges(source, merge_b->target,
9441 children_with_mergeinfo,
9442 merge_b->ctx, scratch_pool, iterpool));
9444 /* remove_noop_subtree_ranges() and/or fix_deleted_subtree_range()
9445 may have further refined the starting revision for our editor
9448 get_most_inclusive_rev(children_with_mergeinfo,
9451 /* Is there anything to merge? */
9452 if (SVN_IS_VALID_REVNUM(start_rev))
9454 /* Now examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the oldest
9455 ending revision that actually needs to be merged (for reverse
9456 merges this is the youngest ending revision). */
9457 svn_revnum_t end_rev =
9458 get_most_inclusive_rev(children_with_mergeinfo,
9459 is_rollback, FALSE);
9461 /* While END_REV is valid, do the following:
9463 1. Tweak each NOTIFY_B->CHILDREN_WITH_MERGEINFO element so that
9464 the element's remaining_ranges member has as its first element
9465 a range that ends with end_rev.
9467 2. Starting with start_rev, call drive_merge_report_editor()
9468 on MERGE_B->target->abspath for start_rev:end_rev.
9470 3. Remove the first element from each
9471 NOTIFY_B->CHILDREN_WITH_MERGEINFO element's remaining_ranges
9474 4. Again examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the most
9475 inclusive starting revision that actually needs to be merged and
9476 update start_rev. This prevents us from needlessly contacting the
9477 repository and doing a diff where we describe the entire target
9478 tree as *not* needing any of the requested range. This can happen
9479 whenever we have mergeinfo with gaps in it for the merge source.
9481 5. Again examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the most
9482 inclusive ending revision that actually needs to be merged and
9485 6. Lather, rinse, repeat.
9488 while (end_rev != SVN_INVALID_REVNUM)
9490 merge_source_t *real_source;
9491 svn_merge_range_t *first_target_range
9492 = (target_merge_path->remaining_ranges->nelts == 0 ? NULL
9493 : APR_ARRAY_IDX(target_merge_path->remaining_ranges, 0,
9494 svn_merge_range_t *));
9496 /* Issue #3324: Stop editor abuse! Don't call
9497 drive_merge_report_editor() in such a way that we request an
9498 editor with svn_client__get_diff_editor() for some rev X,
9499 then call svn_ra_do_diff3() for some revision Y, and then
9500 call reporter->set_path(PATH=="") to set the root revision
9501 for the editor drive to revision Z where
9502 (X != Z && X < Z < Y). This is bogus because the server will
9503 send us the diff between X:Y but the client is expecting the
9504 diff between Y:Z. See issue #3324 for full details on the
9505 problems this can cause. */
9506 if (first_target_range
9507 && start_rev != first_target_range->start)
9511 if (end_rev < first_target_range->start)
9512 end_rev = first_target_range->start;
9516 if (end_rev > first_target_range->start)
9517 end_rev = first_target_range->start;
9521 svn_pool_clear(iterpool);
9523 slice_remaining_ranges(children_with_mergeinfo,
9524 is_rollback, end_rev, scratch_pool);
9526 /* Reset variables that must be reset for every drive */
9527 merge_b->notify_begin.last_abspath = NULL;
9529 real_source = subrange_source(source, start_rev, end_rev, iterpool);
9530 SVN_ERR(drive_merge_report_editor(
9531 merge_b->target->abspath,
9533 children_with_mergeinfo,
9539 /* If any paths picked up explicit mergeinfo as a result of
9540 the merge we need to make sure any mergeinfo those paths
9541 inherited is recorded and then add these paths to
9542 NOTIFY_B->CHILDREN_WITH_MERGEINFO.*/
9543 SVN_ERR(process_children_with_new_mergeinfo(
9544 merge_b, children_with_mergeinfo,
9547 /* If any subtrees had their explicit mergeinfo deleted as a
9548 result of the merge then remove these paths from
9549 NOTIFY_B->CHILDREN_WITH_MERGEINFO since there is no need
9550 to consider these subtrees for subsequent editor drives
9551 nor do we want to record mergeinfo on them describing
9552 the merge itself. */
9553 remove_children_with_deleted_mergeinfo(
9554 merge_b, children_with_mergeinfo);
9556 /* Prepare for the next iteration (if any). */
9557 remove_first_range_from_remaining_ranges(
9558 end_rev, children_with_mergeinfo, scratch_pool);
9560 /* If we raised any conflicts, break out and report how much
9562 if (is_path_conflicted_by_merge(merge_b))
9564 merge_source_t *remaining_range = NULL;
9566 if (real_source->loc2->rev != source->loc2->rev)
9567 remaining_range = subrange_source(source,
9568 real_source->loc2->rev,
9571 *conflict_report = single_range_conflict_report_create(
9572 real_source, remaining_range,
9575 range.end = end_rev;
9580 get_most_inclusive_rev(children_with_mergeinfo,
9583 get_most_inclusive_rev(children_with_mergeinfo,
9584 is_rollback, FALSE);
9587 svn_pool_destroy(iterpool);
9591 if (!merge_b->record_only)
9593 /* Reset the last notification path so that subsequent cherry
9594 picked revision ranges will be notified upon subsequent
9596 merge_b->notify_begin.last_abspath = NULL;
9598 SVN_ERR(drive_merge_report_editor(merge_b->target->abspath,
9608 /* Record mergeinfo where appropriate.*/
9609 if (RECORD_MERGEINFO(merge_b))
9611 const svn_client__pathrev_t *primary_src
9612 = is_rollback ? source->loc1 : source->loc2;
9613 const char *mergeinfo_path
9614 = svn_client__pathrev_fspath(primary_src, scratch_pool);
9616 SVN_ERR(record_mergeinfo_for_dir_merge(result_catalog,
9619 children_with_mergeinfo,
9621 squelch_mergeinfo_notifications,
9625 /* If a path has an immediate parent with non-inheritable mergeinfo at
9626 this point, then it meets criteria 3 or 5 described in
9627 get_mergeinfo_paths' doc string. For paths which exist prior to a
9628 merge explicit mergeinfo has already been set. But for paths added
9629 during the merge this is not the case. The path might have explicit
9630 mergeinfo from the merge source, but no mergeinfo yet exists
9631 describing *this* merge. So the added path has either incomplete
9632 explicit mergeinfo or inherits incomplete mergeinfo from its
9633 immediate parent (if any, the parent might have only non-inheritable
9634 ranges in which case the path simply inherits empty mergeinfo).
9636 So here we look at the root path of each subtree added during the
9637 merge and set explicit mergeinfo on it if it meets the aforementioned
9639 if (range.start < range.end) /* Nothing to record on added subtrees
9640 resulting from reverse merges. */
9642 SVN_ERR(record_mergeinfo_for_added_subtrees(
9643 &range, mergeinfo_path, depth,
9644 squelch_mergeinfo_notifications,
9645 merge_b->added_abspaths, merge_b, scratch_pool));
9649 return SVN_NO_ERROR;
9652 /* Helper for do_merge() when the merge target is a directory.
9654 * If any conflict is raised during the merge, set *CONFLICTED_RANGE to
9655 * the revision sub-range that raised the conflict. In this case, the
9656 * merge will have ended at revision CONFLICTED_RANGE and mergeinfo will
9657 * have been recorded for all revision sub-ranges up to and including
9658 * CONFLICTED_RANGE. Otherwise, set *CONFLICTED_RANGE to NULL.
9660 static svn_error_t *
9661 do_directory_merge(svn_mergeinfo_catalog_t result_catalog,
9662 single_range_conflict_report_t **conflict_report,
9663 const merge_source_t *source,
9664 const char *target_abspath,
9665 const svn_diff_tree_processor_t *processor,
9667 svn_boolean_t squelch_mergeinfo_notifications,
9668 merge_cmd_baton_t *merge_b,
9669 apr_pool_t *result_pool,
9670 apr_pool_t *scratch_pool)
9672 apr_array_header_t *children_with_mergeinfo;
9674 /* Initialize CHILDREN_WITH_MERGEINFO. See the comment
9675 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */
9676 children_with_mergeinfo =
9677 apr_array_make(scratch_pool, 16, sizeof(svn_client__merge_path_t *));
9679 /* And make it read-only accessible from the baton */
9680 merge_b->notify_begin.nodes_with_mergeinfo = children_with_mergeinfo;
9682 /* If we are not honoring mergeinfo we can skip right to the
9683 business of merging changes! */
9684 if (HONOR_MERGEINFO(merge_b))
9685 SVN_ERR(do_mergeinfo_aware_dir_merge(result_catalog, conflict_report,
9686 source, target_abspath,
9687 children_with_mergeinfo,
9689 squelch_mergeinfo_notifications,
9690 merge_b, result_pool, scratch_pool));
9692 SVN_ERR(do_mergeinfo_unaware_dir_merge(conflict_report,
9693 source, target_abspath,
9694 children_with_mergeinfo,
9696 merge_b, result_pool, scratch_pool));
9698 merge_b->notify_begin.nodes_with_mergeinfo = NULL;
9700 return SVN_NO_ERROR;
9703 /** Ensure that *RA_SESSION is opened to URL, either by reusing
9704 * *RA_SESSION if it is non-null and already opened to URL's
9705 * repository, or by allocating a new *RA_SESSION in POOL.
9706 * (RA_SESSION itself cannot be null, of course.)
9708 * CTX is used as for svn_client_open_ra_session().
9710 static svn_error_t *
9711 ensure_ra_session_url(svn_ra_session_t **ra_session,
9713 const char *wri_abspath,
9714 svn_client_ctx_t *ctx,
9717 svn_error_t *err = SVN_NO_ERROR;
9721 err = svn_ra_reparent(*ra_session, url, pool);
9724 /* SVN_ERR_RA_ILLEGAL_URL is raised when url doesn't point to the same
9725 repository as ra_session. */
9726 if (! *ra_session || (err && err->apr_err == SVN_ERR_RA_ILLEGAL_URL))
9728 svn_error_clear(err);
9729 err = svn_client_open_ra_session2(ra_session, url, wri_abspath,
9734 return SVN_NO_ERROR;
9737 /* Drive a merge of MERGE_SOURCES into working copy node TARGET
9738 and possibly record mergeinfo describing the merge -- see
9741 If MODIFIED_SUBTREES is not NULL and all the MERGE_SOURCES are 'ancestral'
9742 or REINTEGRATE_MERGE is true, then replace *MODIFIED_SUBTREES with a new
9743 hash containing all the paths that *MODIFIED_SUBTREES contained before,
9744 and also every path modified, skipped, added, or tree-conflicted
9745 by the merge. Keys and values of the hash are both (const char *)
9746 absolute paths. The contents of the hash are allocated in RESULT_POOL.
9748 If the merge raises any conflicts while merging a revision range, return
9749 early and set *CONFLICT_REPORT to describe the details. (In this case,
9750 notify that the merge is complete if and only if this was the last
9751 revision range of the merge.) If there are no conflicts, set
9752 *CONFLICT_REPORT to NULL. A revision range here can be one specified
9753 in MERGE_SOURCES or an internally generated sub-range of one of those
9754 when merge tracking is in use.
9756 For every (const merge_source_t *) merge source in MERGE_SOURCES, if
9757 SOURCE->ANCESTRAL is set, then the "left" and "right" side are
9758 ancestrally related. (See 'MERGEINFO MERGE SOURCE NORMALIZATION'
9759 for more on what that means and how it matters.)
9761 If SOURCES_RELATED is set, the "left" and "right" sides of the
9762 merge source are historically related (ancestors, uncles, second
9763 cousins thrice removed, etc...). (This is passed through to
9764 do_file_merge() to simulate the history checks that the repository
9765 logic does in the directory case.)
9767 SAME_REPOS is TRUE iff the merge sources live in the same
9768 repository as the one from which the target working copy has been
9771 If mergeinfo is being recorded, SQUELCH_MERGEINFO_NOTIFICATIONS is FALSE,
9772 and CTX->NOTIFY_FUNC2 is not NULL, then call CTX->NOTIFY_FUNC2 with
9773 CTX->NOTIFY_BATON2 and a svn_wc_notify_merge_record_info_begin
9774 notification before any mergeinfo changes are made to describe the merge
9777 If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
9778 is not NULL, then don't record the new mergeinfo on the WC, but instead
9779 record it in RESULT_CATALOG, where the keys are absolute working copy
9780 paths and the values are the new mergeinfos for each. Allocate additions
9781 to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
9783 FORCE_DELETE, DRY_RUN, RECORD_ONLY, DEPTH, MERGE_OPTIONS,
9784 and CTX are as described in the docstring for svn_client_merge_peg3().
9786 If IGNORE_MERGEINFO is true, disable merge tracking, by treating the two
9787 sources as unrelated even if they actually have a common ancestor. See
9788 the macro HONOR_MERGEINFO().
9790 If DIFF_IGNORE_ANCESTRY is true, diff the 'left' and 'right' versions
9791 of a node (if they are the same kind) as if they were related, even if
9792 they are not related. Otherwise, diff unrelated items as a deletion
9793 of one thing and the addition of another.
9795 If not NULL, RECORD_ONLY_PATHS is a hash of (const char *) paths mapped
9796 to the same. If RECORD_ONLY is true and RECORD_ONLY_PATHS is not NULL,
9797 then record mergeinfo describing the merge only on subtrees which contain
9798 items from RECORD_ONLY_PATHS. If RECORD_ONLY is true and RECORD_ONLY_PATHS
9799 is NULL, then record mergeinfo on every subtree with mergeinfo in
9802 REINTEGRATE_MERGE is TRUE if this is a reintegrate merge.
9804 *USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
9805 integrity, *USE_SLEEP will be unchanged if no sleep is required.
9807 SCRATCH_POOL is used for all temporary allocations.
9809 static svn_error_t *
9810 do_merge(apr_hash_t **modified_subtrees,
9811 svn_mergeinfo_catalog_t result_catalog,
9812 conflict_report_t **conflict_report,
9813 svn_boolean_t *use_sleep,
9814 const apr_array_header_t *merge_sources,
9815 const merge_target_t *target,
9816 svn_ra_session_t *src_session,
9817 svn_boolean_t sources_related,
9818 svn_boolean_t same_repos,
9819 svn_boolean_t ignore_mergeinfo,
9820 svn_boolean_t diff_ignore_ancestry,
9821 svn_boolean_t force_delete,
9822 svn_boolean_t dry_run,
9823 svn_boolean_t record_only,
9824 apr_hash_t *record_only_paths,
9825 svn_boolean_t reintegrate_merge,
9826 svn_boolean_t squelch_mergeinfo_notifications,
9828 const apr_array_header_t *merge_options,
9829 svn_client_ctx_t *ctx,
9830 apr_pool_t *result_pool,
9831 apr_pool_t *scratch_pool)
9833 merge_cmd_baton_t merge_cmd_baton = { 0 };
9835 const char *diff3_cmd;
9836 const char *preserved_exts_str;
9838 svn_boolean_t checked_mergeinfo_capability = FALSE;
9839 svn_ra_session_t *ra_session1 = NULL, *ra_session2 = NULL;
9840 const char *old_src_session_url = NULL;
9841 apr_pool_t *iterpool;
9842 const svn_diff_tree_processor_t *processor;
9844 SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath));
9846 *conflict_report = NULL;
9848 /* Check from some special conditions when in record-only mode
9849 (which is a merge-tracking thing). */
9852 svn_boolean_t sources_ancestral = TRUE;
9855 /* Find out whether all of the sources are 'ancestral'. */
9856 for (j = 0; j < merge_sources->nelts; j++)
9857 if (! APR_ARRAY_IDX(merge_sources, j, merge_source_t *)->ancestral)
9859 sources_ancestral = FALSE;
9863 /* We can't do a record-only merge if the sources aren't related. */
9864 if (! sources_ancestral)
9865 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
9866 _("Use of two URLs is not compatible with "
9867 "mergeinfo modification"));
9869 /* We can't do a record-only merge if the sources aren't from
9870 the same repository as the target. */
9872 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
9873 _("Merge from foreign repository is not "
9874 "compatible with mergeinfo modification"));
9876 /* If this is a dry-run record-only merge, there's nothing to do. */
9878 return SVN_NO_ERROR;
9881 iterpool = svn_pool_create(scratch_pool);
9883 /* Ensure a known depth. */
9884 if (depth == svn_depth_unknown)
9885 depth = svn_depth_infinity;
9887 /* Set up the diff3 command, so various callers don't have to. */
9889 ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG)
9891 svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS,
9892 SVN_CONFIG_OPTION_DIFF3_CMD, NULL);
9894 if (diff3_cmd != NULL)
9895 SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool));
9897 /* See which files the user wants to preserve the extension of when
9898 conflict files are made. */
9899 svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY,
9900 SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, "");
9902 /* Build the merge context baton (or at least the parts of it that
9903 don't need to be reset for each merge source). */
9904 merge_cmd_baton.force_delete = force_delete;
9905 merge_cmd_baton.dry_run = dry_run;
9906 merge_cmd_baton.record_only = record_only;
9907 merge_cmd_baton.ignore_mergeinfo = ignore_mergeinfo;
9908 merge_cmd_baton.diff_ignore_ancestry = diff_ignore_ancestry;
9909 merge_cmd_baton.same_repos = same_repos;
9910 merge_cmd_baton.mergeinfo_capable = FALSE;
9911 merge_cmd_baton.ctx = ctx;
9912 merge_cmd_baton.reintegrate_merge = reintegrate_merge;
9913 merge_cmd_baton.target = target;
9914 merge_cmd_baton.pool = iterpool;
9915 merge_cmd_baton.merge_options = merge_options;
9916 merge_cmd_baton.diff3_cmd = diff3_cmd;
9917 merge_cmd_baton.ext_patterns = *preserved_exts_str
9918 ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ",
9919 FALSE, scratch_pool)
9922 merge_cmd_baton.use_sleep = use_sleep;
9924 /* Do we already know the specific subtrees with mergeinfo we want
9925 to record-only mergeinfo on? */
9926 if (record_only && record_only_paths)
9927 merge_cmd_baton.merged_abspaths = record_only_paths;
9929 merge_cmd_baton.merged_abspaths = apr_hash_make(result_pool);
9931 merge_cmd_baton.skipped_abspaths = apr_hash_make(result_pool);
9932 merge_cmd_baton.added_abspaths = apr_hash_make(result_pool);
9933 merge_cmd_baton.tree_conflicted_abspaths = apr_hash_make(result_pool);
9936 svn_diff_tree_processor_t *merge_processor;
9938 merge_processor = svn_diff__tree_processor_create(&merge_cmd_baton,
9941 merge_processor->dir_opened = merge_dir_opened;
9942 merge_processor->dir_changed = merge_dir_changed;
9943 merge_processor->dir_added = merge_dir_added;
9944 merge_processor->dir_deleted = merge_dir_deleted;
9945 merge_processor->dir_closed = merge_dir_closed;
9947 merge_processor->file_opened = merge_file_opened;
9948 merge_processor->file_changed = merge_file_changed;
9949 merge_processor->file_added = merge_file_added;
9950 merge_processor->file_deleted = merge_file_deleted;
9951 /* Not interested in file_closed() */
9953 merge_processor->node_absent = merge_node_absent;
9955 processor = merge_processor;
9960 SVN_ERR(svn_ra_get_session_url(src_session, &old_src_session_url,
9962 ra_session1 = src_session;
9965 for (i = 0; i < merge_sources->nelts; i++)
9967 svn_node_kind_t src1_kind;
9968 merge_source_t *source =
9969 APR_ARRAY_IDX(merge_sources, i, merge_source_t *);
9970 single_range_conflict_report_t *conflicted_range_report;
9972 svn_pool_clear(iterpool);
9974 /* Sanity check: if our left- and right-side merge sources are
9975 the same, there's nothing to here. */
9976 if ((strcmp(source->loc1->url, source->loc2->url) == 0)
9977 && (source->loc1->rev == source->loc2->rev))
9980 /* Establish RA sessions to our URLs, reuse where possible. */
9981 SVN_ERR(ensure_ra_session_url(&ra_session1, source->loc1->url,
9982 target->abspath, ctx, scratch_pool));
9983 SVN_ERR(ensure_ra_session_url(&ra_session2, source->loc2->url,
9984 target->abspath, ctx, scratch_pool));
9986 /* Populate the portions of the merge context baton that need to
9987 be reset for each merge source iteration. */
9988 merge_cmd_baton.merge_source = *source;
9989 merge_cmd_baton.implicit_src_gap = NULL;
9990 merge_cmd_baton.conflicted_paths = NULL;
9991 merge_cmd_baton.paths_with_new_mergeinfo = NULL;
9992 merge_cmd_baton.paths_with_deleted_mergeinfo = NULL;
9993 merge_cmd_baton.ra_session1 = ra_session1;
9994 merge_cmd_baton.ra_session2 = ra_session2;
9996 merge_cmd_baton.notify_begin.last_abspath = NULL;
9998 /* Populate the portions of the merge context baton that require
9999 an RA session to set, but shouldn't be reset for each iteration. */
10000 if (! checked_mergeinfo_capability)
10002 SVN_ERR(svn_ra_has_capability(ra_session1,
10003 &merge_cmd_baton.mergeinfo_capable,
10004 SVN_RA_CAPABILITY_MERGEINFO,
10006 checked_mergeinfo_capability = TRUE;
10009 SVN_ERR(svn_ra_check_path(ra_session1, "", source->loc1->rev,
10010 &src1_kind, iterpool));
10012 /* Run the merge; if there are conflicts, allow the callback to
10013 * resolve them, and if it resolves all of them, then run the
10014 * merge again with the remaining revision range, until it is all
10018 /* Merge as far as possible without resolving any conflicts */
10019 if (src1_kind != svn_node_dir)
10021 SVN_ERR(do_file_merge(result_catalog, &conflicted_range_report,
10022 source, target->abspath,
10025 squelch_mergeinfo_notifications,
10026 &merge_cmd_baton, iterpool, iterpool));
10028 else /* Directory */
10030 SVN_ERR(do_directory_merge(result_catalog, &conflicted_range_report,
10031 source, target->abspath,
10033 depth, squelch_mergeinfo_notifications,
10034 &merge_cmd_baton, iterpool, iterpool));
10037 /* Give the conflict resolver callback the opportunity to
10038 * resolve any conflicts that were raised. If it resolves all
10039 * of them, go around again to merge the next sub-range (if any). */
10040 if (conflicted_range_report && ctx->conflict_func2 && ! dry_run)
10042 svn_boolean_t conflicts_remain;
10044 SVN_ERR(svn_client__resolve_conflicts(
10045 &conflicts_remain, merge_cmd_baton.conflicted_paths,
10047 if (conflicts_remain)
10050 merge_cmd_baton.conflicted_paths = NULL;
10051 /* Caution: this source is in iterpool */
10052 source = conflicted_range_report->remaining_source;
10053 conflicted_range_report = NULL;
10060 /* The final mergeinfo on TARGET_WCPATH may itself elide. */
10062 SVN_ERR(svn_client__elide_mergeinfo(target->abspath, NULL,
10065 /* If conflicts occurred while merging any but the very last
10066 * range of a multi-pass merge, we raise an error that aborts
10067 * the merge. The user will be asked to resolve conflicts
10068 * before merging subsequent revision ranges. */
10069 if (conflicted_range_report)
10071 *conflict_report = conflict_report_create(
10072 target->abspath, conflicted_range_report->conflicted_range,
10073 (i == merge_sources->nelts - 1
10074 && ! conflicted_range_report->remaining_source),
10080 if (! *conflict_report || (*conflict_report)->was_last_range)
10082 /* Let everyone know we're finished here. */
10083 notify_merge_completed(target->abspath, ctx, iterpool);
10086 /* Does the caller want to know what the merge has done? */
10087 if (modified_subtrees)
10089 *modified_subtrees =
10090 apr_hash_overlay(result_pool, *modified_subtrees,
10091 merge_cmd_baton.merged_abspaths);
10092 *modified_subtrees =
10093 apr_hash_overlay(result_pool, *modified_subtrees,
10094 merge_cmd_baton.added_abspaths);
10095 *modified_subtrees =
10096 apr_hash_overlay(result_pool, *modified_subtrees,
10097 merge_cmd_baton.skipped_abspaths);
10098 *modified_subtrees =
10099 apr_hash_overlay(result_pool, *modified_subtrees,
10100 merge_cmd_baton.tree_conflicted_abspaths);
10104 SVN_ERR(svn_ra_reparent(src_session, old_src_session_url, iterpool));
10106 svn_pool_destroy(iterpool);
10107 return SVN_NO_ERROR;
10110 /* Perform a two-URL merge between URLs which are related, but neither
10111 is a direct ancestor of the other. This first does a real two-URL
10112 merge (unless this is record-only), followed by record-only merges
10113 to represent the changed mergeinfo.
10115 Set *CONFLICT_REPORT to indicate if there were any conflicts, as in
10118 The diff to be merged is between SOURCE->loc1 (in URL1_RA_SESSION1)
10119 and SOURCE->loc2 (in URL2_RA_SESSION2); YCA is their youngest
10122 SAME_REPOS must be true if and only if the source URLs are in the same
10123 repository as the target working copy.
10125 DIFF_IGNORE_ANCESTRY is as in do_merge().
10127 Other arguments are as in all of the public merge APIs.
10129 *USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
10130 integrity, *USE_SLEEP will be unchanged if no sleep is required.
10132 SCRATCH_POOL is used for all temporary allocations.
10134 static svn_error_t *
10135 merge_cousins_and_supplement_mergeinfo(conflict_report_t **conflict_report,
10136 svn_boolean_t *use_sleep,
10137 const merge_target_t *target,
10138 svn_ra_session_t *URL1_ra_session,
10139 svn_ra_session_t *URL2_ra_session,
10140 const merge_source_t *source,
10141 const svn_client__pathrev_t *yca,
10142 svn_boolean_t same_repos,
10144 svn_boolean_t diff_ignore_ancestry,
10145 svn_boolean_t force_delete,
10146 svn_boolean_t record_only,
10147 svn_boolean_t dry_run,
10148 const apr_array_header_t *merge_options,
10149 svn_client_ctx_t *ctx,
10150 apr_pool_t *result_pool,
10151 apr_pool_t *scratch_pool)
10153 apr_array_header_t *remove_sources, *add_sources;
10154 apr_hash_t *modified_subtrees = NULL;
10156 /* Sure we could use SCRATCH_POOL throughout this function, but since this
10157 is a wrapper around three separate merges we'll create a subpool we can
10158 clear between each of the three. If the merge target has a lot of
10159 subtree mergeinfo, then this will help keep memory use in check. */
10160 apr_pool_t *subpool = svn_pool_create(scratch_pool);
10162 assert(session_url_is(URL1_ra_session, source->loc1->url, scratch_pool));
10163 assert(session_url_is(URL2_ra_session, source->loc2->url, scratch_pool));
10165 SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath));
10166 SVN_ERR_ASSERT(! source->ancestral);
10168 SVN_ERR(normalize_merge_sources_internal(
10169 &remove_sources, source->loc1,
10170 svn_rangelist__initialize(source->loc1->rev, yca->rev, TRUE,
10172 URL1_ra_session, ctx, scratch_pool, subpool));
10174 SVN_ERR(normalize_merge_sources_internal(
10175 &add_sources, source->loc2,
10176 svn_rangelist__initialize(yca->rev, source->loc2->rev, TRUE,
10178 URL2_ra_session, ctx, scratch_pool, subpool));
10180 *conflict_report = NULL;
10182 /* If this isn't a record-only merge, we'll first do a stupid
10183 point-to-point merge... */
10186 apr_array_header_t *faux_sources =
10187 apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
10189 modified_subtrees = apr_hash_make(scratch_pool);
10190 APR_ARRAY_PUSH(faux_sources, const merge_source_t *) = source;
10191 SVN_ERR(do_merge(&modified_subtrees, NULL, conflict_report, use_sleep,
10192 faux_sources, target,
10193 URL1_ra_session, TRUE, same_repos,
10194 FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10195 force_delete, dry_run, FALSE, NULL, TRUE,
10196 FALSE, depth, merge_options, ctx,
10197 scratch_pool, subpool));
10198 if (*conflict_report)
10200 *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10201 if (! (*conflict_report)->was_last_range)
10202 return SVN_NO_ERROR;
10205 else if (! same_repos)
10207 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
10208 _("Merge from foreign repository is not "
10209 "compatible with mergeinfo modification"));
10212 /* ... and now, if we're doing the mergeinfo thang, we execute a
10213 pair of record-only merges using the real sources we've
10216 Issue #3648: We don't actually perform these two record-only merges
10217 on the WC at first, but rather see what each would do and store that
10218 in two mergeinfo catalogs. We then merge the catalogs together and
10219 then record the result in the WC. This prevents the second record
10220 only merge from removing legitimate mergeinfo history, from the same
10221 source, that was made in prior merges. */
10222 if (same_repos && !dry_run)
10224 svn_mergeinfo_catalog_t add_result_catalog =
10225 apr_hash_make(scratch_pool);
10226 svn_mergeinfo_catalog_t remove_result_catalog =
10227 apr_hash_make(scratch_pool);
10229 notify_mergeinfo_recording(target->abspath, NULL, ctx, scratch_pool);
10230 svn_pool_clear(subpool);
10231 SVN_ERR(do_merge(NULL, add_result_catalog, conflict_report, use_sleep,
10232 add_sources, target,
10233 URL1_ra_session, TRUE, same_repos,
10234 FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10235 force_delete, dry_run, TRUE,
10236 modified_subtrees, TRUE,
10237 TRUE, depth, merge_options, ctx,
10238 scratch_pool, subpool));
10239 if (*conflict_report)
10241 *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10242 if (! (*conflict_report)->was_last_range)
10243 return SVN_NO_ERROR;
10245 svn_pool_clear(subpool);
10246 SVN_ERR(do_merge(NULL, remove_result_catalog, conflict_report, use_sleep,
10247 remove_sources, target,
10248 URL1_ra_session, TRUE, same_repos,
10249 FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10250 force_delete, dry_run, TRUE,
10251 modified_subtrees, TRUE,
10252 TRUE, depth, merge_options, ctx,
10253 scratch_pool, subpool));
10254 if (*conflict_report)
10256 *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10257 if (! (*conflict_report)->was_last_range)
10258 return SVN_NO_ERROR;
10260 SVN_ERR(svn_mergeinfo_catalog_merge(add_result_catalog,
10261 remove_result_catalog,
10262 scratch_pool, scratch_pool));
10263 SVN_ERR(svn_client__record_wc_mergeinfo_catalog(add_result_catalog,
10264 ctx, scratch_pool));
10267 svn_pool_destroy(subpool);
10268 return SVN_NO_ERROR;
10271 /* Perform checks to determine whether the working copy at TARGET_ABSPATH
10272 * can safely be used as a merge target. Checks are performed according to
10273 * the ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, and ALLOW_SWITCHED_SUBTREES
10274 * parameters. If any checks fail, raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE.
10276 * E.g. if all the ALLOW_* parameters are FALSE, TARGET_ABSPATH must
10277 * be a single-revision, pristine, unswitched working copy.
10278 * In other words, it must reflect a subtree of the repository as found
10279 * at single revision -- although sparse checkouts are permitted. */
10280 static svn_error_t *
10281 ensure_wc_is_suitable_merge_target(const char *target_abspath,
10282 svn_client_ctx_t *ctx,
10283 svn_boolean_t allow_mixed_rev,
10284 svn_boolean_t allow_local_mods,
10285 svn_boolean_t allow_switched_subtrees,
10286 apr_pool_t *scratch_pool)
10288 svn_node_kind_t target_kind;
10290 /* Check the target exists. */
10291 SVN_ERR(svn_io_check_path(target_abspath, &target_kind, scratch_pool));
10292 if (target_kind == svn_node_none)
10293 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
10294 _("Path '%s' does not exist"),
10295 svn_dirent_local_style(target_abspath,
10297 SVN_ERR(svn_wc_read_kind2(&target_kind, ctx->wc_ctx, target_abspath,
10298 FALSE, FALSE, scratch_pool));
10299 if (target_kind != svn_node_dir && target_kind != svn_node_file)
10300 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
10301 _("Merge target '%s' does not exist in the "
10302 "working copy"), target_abspath);
10304 /* Perform the mixed-revision check first because it's the cheapest one. */
10305 if (! allow_mixed_rev)
10307 svn_revnum_t min_rev;
10308 svn_revnum_t max_rev;
10310 SVN_ERR(svn_client_min_max_revisions(&min_rev, &max_rev, target_abspath,
10311 FALSE, ctx, scratch_pool));
10313 if (!(SVN_IS_VALID_REVNUM(min_rev) && SVN_IS_VALID_REVNUM(max_rev)))
10315 svn_boolean_t is_added;
10317 /* Allow merge into added nodes. */
10318 SVN_ERR(svn_wc__node_is_added(&is_added, ctx->wc_ctx, target_abspath,
10321 return SVN_NO_ERROR;
10323 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10324 _("Cannot determine revision of working "
10328 if (min_rev != max_rev)
10329 return svn_error_createf(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
10330 _("Cannot merge into mixed-revision working "
10331 "copy [%ld:%ld]; try updating first"),
10335 /* Next, check for switched subtrees. */
10336 if (! allow_switched_subtrees)
10338 svn_boolean_t is_switched;
10340 SVN_ERR(svn_wc__has_switched_subtrees(&is_switched, ctx->wc_ctx,
10341 target_abspath, NULL,
10344 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10345 _("Cannot merge into a working copy "
10346 "with a switched subtree"));
10349 /* This is the most expensive check, so it is performed last.*/
10350 if (! allow_local_mods)
10352 svn_boolean_t is_modified;
10354 SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx,
10355 target_abspath, TRUE,
10360 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10361 _("Cannot merge into a working copy "
10362 "that has local modifications"));
10365 return SVN_NO_ERROR;
10368 /* Throw an error if PATH_OR_URL is a path and REVISION isn't a repository
10370 static svn_error_t *
10371 ensure_wc_path_has_repo_revision(const char *path_or_url,
10372 const svn_opt_revision_t *revision,
10373 apr_pool_t *scratch_pool)
10375 if (revision->kind != svn_opt_revision_number
10376 && revision->kind != svn_opt_revision_date
10377 && revision->kind != svn_opt_revision_head
10378 && ! svn_path_is_url(path_or_url))
10379 return svn_error_createf(
10380 SVN_ERR_CLIENT_BAD_REVISION, NULL,
10381 _("Invalid merge source '%s'; a working copy path can only be "
10382 "used with a repository revision (a number, a date, or head)"),
10383 svn_dirent_local_style(path_or_url, scratch_pool));
10384 return SVN_NO_ERROR;
10387 /* "Open" the target WC for a merge. That means:
10388 * - find out its exact repository location
10389 * - check the WC for suitability (throw an error if unsuitable)
10391 * Set *TARGET_P to a new, fully initialized, target description structure.
10393 * ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, ALLOW_SWITCHED_SUBTREES determine
10394 * whether the WC is deemed suitable; see ensure_wc_is_suitable_merge_target()
10397 * If the node is locally added, the rev and URL will be null/invalid. Some
10398 * kinds of merge can use such a target; others can't.
10400 static svn_error_t *
10401 open_target_wc(merge_target_t **target_p,
10402 const char *wc_abspath,
10403 svn_boolean_t allow_mixed_rev,
10404 svn_boolean_t allow_local_mods,
10405 svn_boolean_t allow_switched_subtrees,
10406 svn_client_ctx_t *ctx,
10407 apr_pool_t *result_pool,
10408 apr_pool_t *scratch_pool)
10410 merge_target_t *target = apr_palloc(result_pool, sizeof(*target));
10411 svn_client__pathrev_t *origin;
10413 target->abspath = apr_pstrdup(result_pool, wc_abspath);
10415 SVN_ERR(svn_client__wc_node_get_origin(&origin, wc_abspath, ctx,
10416 result_pool, scratch_pool));
10419 target->loc = *origin;
10424 /* The node has no location in the repository. It's unversioned or
10425 * locally added or locally deleted.
10427 * If it's locally added or deleted, find the repository root
10428 * URL and UUID anyway, and leave the node URL and revision as NULL
10429 * and INVALID. If it's unversioned, this will throw an error. */
10430 err = svn_wc__node_get_repos_info(NULL, NULL,
10431 &target->loc.repos_root_url,
10432 &target->loc.repos_uuid,
10433 ctx->wc_ctx, wc_abspath,
10434 result_pool, scratch_pool);
10438 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
10439 && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY
10440 && err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
10441 return svn_error_trace(err);
10443 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, err,
10444 _("Merge target '%s' does not exist in the "
10446 svn_dirent_local_style(wc_abspath,
10450 target->loc.rev = SVN_INVALID_REVNUM;
10451 target->loc.url = NULL;
10454 SVN_ERR(ensure_wc_is_suitable_merge_target(
10456 allow_mixed_rev, allow_local_mods, allow_switched_subtrees,
10459 *target_p = target;
10460 return SVN_NO_ERROR;
10463 /*-----------------------------------------------------------------------*/
10465 /*** Public APIs ***/
10467 /* The body of svn_client_merge5(), which see for details.
10469 * If SOURCE1 @ REVISION1 is related to SOURCE2 @ REVISION2 then use merge
10470 * tracking (subject to other constraints -- see HONOR_MERGEINFO());
10471 * otherwise disable merge tracking.
10473 * IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge().
10475 static svn_error_t *
10476 merge_locked(conflict_report_t **conflict_report,
10477 const char *source1,
10478 const svn_opt_revision_t *revision1,
10479 const char *source2,
10480 const svn_opt_revision_t *revision2,
10481 const char *target_abspath,
10483 svn_boolean_t ignore_mergeinfo,
10484 svn_boolean_t diff_ignore_ancestry,
10485 svn_boolean_t force_delete,
10486 svn_boolean_t record_only,
10487 svn_boolean_t dry_run,
10488 svn_boolean_t allow_mixed_rev,
10489 const apr_array_header_t *merge_options,
10490 svn_client_ctx_t *ctx,
10491 apr_pool_t *result_pool,
10492 apr_pool_t *scratch_pool)
10494 merge_target_t *target;
10495 svn_client__pathrev_t *source1_loc, *source2_loc;
10496 svn_boolean_t sources_related = FALSE;
10497 svn_ra_session_t *ra_session1, *ra_session2;
10498 apr_array_header_t *merge_sources;
10500 svn_boolean_t use_sleep = FALSE;
10501 svn_client__pathrev_t *yca = NULL;
10502 apr_pool_t *sesspool;
10503 svn_boolean_t same_repos;
10505 /* ### FIXME: This function really ought to do a history check on
10506 the left and right sides of the merge source, and -- if one is an
10507 ancestor of the other -- just call svn_client_merge_peg3() with
10508 the appropriate args. */
10510 SVN_ERR(open_target_wc(&target, target_abspath,
10511 allow_mixed_rev, TRUE, TRUE,
10512 ctx, scratch_pool, scratch_pool));
10514 /* Open RA sessions to both sides of our merge source, and resolve URLs
10515 * and revisions. */
10516 sesspool = svn_pool_create(scratch_pool);
10517 SVN_ERR(svn_client__ra_session_from_path2(
10518 &ra_session1, &source1_loc,
10519 source1, NULL, revision1, revision1, ctx, sesspool));
10520 SVN_ERR(svn_client__ra_session_from_path2(
10521 &ra_session2, &source2_loc,
10522 source2, NULL, revision2, revision2, ctx, sesspool));
10524 /* We can't do a diff between different repositories. */
10525 /* ### We should also insist that the root URLs of the two sources match,
10526 * as we are only carrying around a single source-repos-root from now
10527 * on, and URL calculations will go wrong if they differ.
10528 * Alternatively, teach the code to cope with differing root URLs. */
10529 SVN_ERR(check_same_repos(source1_loc, source1_loc->url,
10530 source2_loc, source2_loc->url,
10531 FALSE /* strict_urls */, scratch_pool));
10533 /* Do our working copy and sources come from the same repository? */
10534 same_repos = is_same_repos(&target->loc, source1_loc, TRUE /* strict_urls */);
10536 /* Unless we're ignoring ancestry, see if the two sources are related. */
10537 if (! ignore_mergeinfo)
10538 SVN_ERR(svn_client__get_youngest_common_ancestor(
10539 &yca, source1_loc, source2_loc, ra_session1, ctx,
10540 scratch_pool, scratch_pool));
10542 /* Check for a youngest common ancestor. If we have one, we'll be
10543 doing merge tracking.
10545 So, given a requested merge of the differences between A and
10546 B, and a common ancestor of C, we will find ourselves in one of
10547 four positions, and four different approaches:
10549 A == B == C there's nothing to merge
10551 A == C != B we merge the changes between A (or C) and B
10553 B == C != A we merge the changes between B (or C) and A
10555 A != B != C we merge the changes between A and B without
10556 merge recording, then record-only two merges:
10557 from A to C, and from C to B
10561 /* Note that our merge sources are related. */
10562 sources_related = TRUE;
10564 /* If the common ancestor matches the right side of our merge,
10565 then we only need to reverse-merge the left side. */
10566 if ((strcmp(yca->url, source2_loc->url) == 0)
10567 && (yca->rev == source2_loc->rev))
10569 SVN_ERR(normalize_merge_sources_internal(
10570 &merge_sources, source1_loc,
10571 svn_rangelist__initialize(source1_loc->rev, yca->rev, TRUE,
10573 ra_session1, ctx, scratch_pool, scratch_pool));
10575 /* If the common ancestor matches the left side of our merge,
10576 then we only need to merge the right side. */
10577 else if ((strcmp(yca->url, source1_loc->url) == 0)
10578 && (yca->rev == source1_loc->rev))
10580 SVN_ERR(normalize_merge_sources_internal(
10581 &merge_sources, source2_loc,
10582 svn_rangelist__initialize(yca->rev, source2_loc->rev, TRUE,
10584 ra_session2, ctx, scratch_pool, scratch_pool));
10586 /* And otherwise, we need to do both: reverse merge the left
10587 side, and merge the right. */
10590 merge_source_t source;
10592 source.loc1 = source1_loc;
10593 source.loc2 = source2_loc;
10594 source.ancestral = FALSE;
10596 err = merge_cousins_and_supplement_mergeinfo(conflict_report,
10605 diff_ignore_ancestry,
10607 record_only, dry_run,
10612 /* Close our temporary RA sessions (this could've happened
10613 after the second call to normalize_merge_sources() inside
10614 the merge_cousins_and_supplement_mergeinfo() routine). */
10615 svn_pool_destroy(sesspool);
10618 svn_io_sleep_for_timestamps(target->abspath, scratch_pool);
10621 return SVN_NO_ERROR;
10626 /* Build a single-item merge_source_t array. */
10627 merge_sources = apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
10628 APR_ARRAY_PUSH(merge_sources, merge_source_t *)
10629 = merge_source_create(source1_loc, source2_loc, FALSE, scratch_pool);
10632 err = do_merge(NULL, NULL, conflict_report, &use_sleep,
10633 merge_sources, target,
10634 ra_session1, sources_related, same_repos,
10635 ignore_mergeinfo, diff_ignore_ancestry, force_delete, dry_run,
10636 record_only, NULL, FALSE, FALSE, depth, merge_options,
10637 ctx, result_pool, scratch_pool);
10639 /* Close our temporary RA sessions. */
10640 svn_pool_destroy(sesspool);
10643 svn_io_sleep_for_timestamps(target->abspath, scratch_pool);
10646 return SVN_NO_ERROR;
10649 /* Set *TARGET_ABSPATH to the absolute path of, and *LOCK_ABSPATH to
10650 the absolute path to lock for, TARGET_WCPATH. */
10651 static svn_error_t *
10652 get_target_and_lock_abspath(const char **target_abspath,
10653 const char **lock_abspath,
10654 const char *target_wcpath,
10655 svn_client_ctx_t *ctx,
10656 apr_pool_t *result_pool)
10658 svn_node_kind_t kind;
10659 SVN_ERR(svn_dirent_get_absolute(target_abspath, target_wcpath,
10661 SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, *target_abspath,
10662 FALSE, FALSE, result_pool));
10663 if (kind == svn_node_dir)
10664 *lock_abspath = *target_abspath;
10666 *lock_abspath = svn_dirent_dirname(*target_abspath, result_pool);
10668 return SVN_NO_ERROR;
10672 svn_client_merge5(const char *source1,
10673 const svn_opt_revision_t *revision1,
10674 const char *source2,
10675 const svn_opt_revision_t *revision2,
10676 const char *target_wcpath,
10678 svn_boolean_t ignore_mergeinfo,
10679 svn_boolean_t diff_ignore_ancestry,
10680 svn_boolean_t force_delete,
10681 svn_boolean_t record_only,
10682 svn_boolean_t dry_run,
10683 svn_boolean_t allow_mixed_rev,
10684 const apr_array_header_t *merge_options,
10685 svn_client_ctx_t *ctx,
10688 const char *target_abspath, *lock_abspath;
10689 conflict_report_t *conflict_report;
10691 /* Sanity check our input -- we require specified revisions,
10692 * and either 2 paths or 2 URLs. */
10693 if ((revision1->kind == svn_opt_revision_unspecified)
10694 || (revision2->kind == svn_opt_revision_unspecified))
10695 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
10696 _("Not all required revisions are specified"));
10697 if (svn_path_is_url(source1) != svn_path_is_url(source2))
10698 return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
10699 _("Merge sources must both be "
10700 "either paths or URLs"));
10701 /* A WC path must be used with a repository revision, as we can't
10702 * (currently) use the WC itself as a source, we can only read the URL
10703 * from it and use that. */
10704 SVN_ERR(ensure_wc_path_has_repo_revision(source1, revision1, pool));
10705 SVN_ERR(ensure_wc_path_has_repo_revision(source2, revision2, pool));
10707 SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
10708 target_wcpath, ctx, pool));
10711 SVN_WC__CALL_WITH_WRITE_LOCK(
10712 merge_locked(&conflict_report,
10713 source1, revision1, source2, revision2,
10714 target_abspath, depth, ignore_mergeinfo,
10715 diff_ignore_ancestry,
10716 force_delete, record_only, dry_run,
10717 allow_mixed_rev, merge_options, ctx, pool, pool),
10718 ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
10720 SVN_ERR(merge_locked(&conflict_report,
10721 source1, revision1, source2, revision2,
10722 target_abspath, depth, ignore_mergeinfo,
10723 diff_ignore_ancestry,
10724 force_delete, record_only, dry_run,
10725 allow_mixed_rev, merge_options, ctx, pool, pool));
10727 SVN_ERR(make_merge_conflict_error(conflict_report, pool));
10728 return SVN_NO_ERROR;
10732 /* Check if mergeinfo for a given path is described explicitly or via
10733 inheritance in a mergeinfo catalog.
10735 If REPOS_REL_PATH exists in CATALOG and has mergeinfo containing
10736 MERGEINFO, then set *IN_CATALOG to TRUE. If REPOS_REL_PATH does
10737 not exist in CATALOG, then find its nearest parent which does exist.
10738 If the mergeinfo REPOS_REL_PATH would inherit from that parent
10739 contains MERGEINFO then set *IN_CATALOG to TRUE. Set *IN_CATALOG
10740 to FALSE in all other cases.
10742 Set *CAT_KEY_PATH to the key path in CATALOG for REPOS_REL_PATH's
10743 explicit or inherited mergeinfo. If no explicit or inherited mergeinfo
10744 is found for REPOS_REL_PATH then set *CAT_KEY_PATH to NULL.
10746 User RESULT_POOL to allocate *CAT_KEY_PATH. Use SCRATCH_POOL for
10747 temporary allocations. */
10748 static svn_error_t *
10749 mergeinfo_in_catalog(svn_boolean_t *in_catalog,
10750 const char **cat_key_path,
10751 const char *repos_rel_path,
10752 svn_mergeinfo_t mergeinfo,
10753 svn_mergeinfo_catalog_t catalog,
10754 apr_pool_t *result_pool,
10755 apr_pool_t *scratch_pool)
10757 const char *walk_path = NULL;
10759 *in_catalog = FALSE;
10760 *cat_key_path = NULL;
10762 if (mergeinfo && catalog && apr_hash_count(catalog))
10764 const char *path = repos_rel_path;
10766 /* Start with the assumption there is no explicit or inherited
10767 mergeinfo for REPOS_REL_PATH in CATALOG. */
10768 svn_mergeinfo_t mergeinfo_in_cat = NULL;
10772 mergeinfo_in_cat = svn_hash_gets(catalog, path);
10774 if (mergeinfo_in_cat) /* Found it! */
10776 *cat_key_path = apr_pstrdup(result_pool, path);
10779 else /* Look for inherited mergeinfo. */
10781 walk_path = svn_relpath_join(svn_relpath_basename(path,
10783 walk_path ? walk_path : "",
10785 path = svn_relpath_dirname(path, scratch_pool);
10787 if (path[0] == '\0') /* No mergeinfo to inherit. */
10792 if (mergeinfo_in_cat)
10795 SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(&mergeinfo_in_cat,
10800 SVN_ERR(svn_mergeinfo_intersect2(&mergeinfo_in_cat,
10801 mergeinfo_in_cat, mergeinfo,
10803 scratch_pool, scratch_pool));
10804 SVN_ERR(svn_mergeinfo__equals(in_catalog, mergeinfo_in_cat,
10805 mergeinfo, TRUE, scratch_pool));
10809 return SVN_NO_ERROR;
10812 /* A svn_log_entry_receiver_t baton for log_find_operative_revs(). */
10813 typedef struct log_find_operative_baton_t
10815 /* The catalog of explicit mergeinfo on a reintegrate source. */
10816 svn_mergeinfo_catalog_t merged_catalog;
10818 /* The catalog of unmerged history from the reintegrate target to
10819 the source which we will create. Allocated in RESULT_POOL. */
10820 svn_mergeinfo_catalog_t unmerged_catalog;
10822 /* The repository absolute path of the reintegrate target. */
10823 const char *target_fspath;
10825 /* The path of the reintegrate source relative to the repository root. */
10826 const char *source_repos_rel_path;
10828 apr_pool_t *result_pool;
10829 } log_find_operative_baton_t;
10831 /* A svn_log_entry_receiver_t callback for find_unsynced_ranges(). */
10832 static svn_error_t *
10833 log_find_operative_revs(void *baton,
10834 svn_log_entry_t *log_entry,
10837 log_find_operative_baton_t *log_baton = baton;
10838 apr_hash_index_t *hi;
10839 svn_revnum_t revision;
10841 /* It's possible that authz restrictions on the merge source prevent us
10842 from knowing about any of the changes for LOG_ENTRY->REVISION. */
10843 if (!log_entry->changed_paths2)
10844 return SVN_NO_ERROR;
10846 revision = log_entry->revision;
10848 for (hi = apr_hash_first(pool, log_entry->changed_paths2);
10850 hi = apr_hash_next(hi))
10852 const char *subtree_missing_this_rev;
10853 const char *path = apr_hash_this_key(hi);
10854 const char *rel_path;
10855 const char *source_rel_path;
10856 svn_boolean_t in_catalog;
10857 svn_mergeinfo_t log_entry_as_mergeinfo;
10859 rel_path = svn_fspath__skip_ancestor(log_baton->target_fspath, path);
10860 /* Easy out: The path is not within the tree of interest. */
10861 if (rel_path == NULL)
10864 source_rel_path = svn_relpath_join(log_baton->source_repos_rel_path,
10867 SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo,
10868 apr_psprintf(pool, "%s:%ld",
10872 SVN_ERR(mergeinfo_in_catalog(&in_catalog, &subtree_missing_this_rev,
10873 source_rel_path, log_entry_as_mergeinfo,
10874 log_baton->merged_catalog,
10879 svn_mergeinfo_t unmerged_for_key;
10880 const char *suffix, *missing_path;
10882 /* If there is no mergeinfo on the source tree we'll say
10883 the "subtree" missing this revision is the root of the
10885 if (!subtree_missing_this_rev)
10886 subtree_missing_this_rev = log_baton->source_repos_rel_path;
10888 suffix = svn_relpath_skip_ancestor(subtree_missing_this_rev,
10890 if (suffix && suffix[0] != '\0')
10892 missing_path = apr_pstrmemdup(pool, path,
10893 strlen(path) - strlen(suffix) - 1);
10897 missing_path = path;
10900 SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo,
10901 apr_psprintf(pool, "%s:%ld",
10902 missing_path, revision),
10903 log_baton->result_pool));
10904 unmerged_for_key = svn_hash_gets(log_baton->unmerged_catalog,
10905 subtree_missing_this_rev);
10907 if (unmerged_for_key)
10909 SVN_ERR(svn_mergeinfo_merge2(unmerged_for_key,
10910 log_entry_as_mergeinfo,
10911 log_baton->result_pool,
10916 svn_hash_sets(log_baton->unmerged_catalog,
10917 apr_pstrdup(log_baton->result_pool,
10918 subtree_missing_this_rev),
10919 log_entry_as_mergeinfo);
10924 return SVN_NO_ERROR;
10927 /* Determine if the mergeinfo on a reintegrate source SOURCE_LOC,
10928 reflects that the source is fully synced with the reintegrate target
10929 TARGET_LOC, even if a naive interpretation of the source's
10930 mergeinfo says otherwise -- See issue #3577.
10932 UNMERGED_CATALOG represents the history (as mergeinfo) from
10933 TARGET_LOC that is not represented in SOURCE_LOC's
10934 explicit/inherited mergeinfo as represented by MERGED_CATALOG.
10935 MERGED_CATALOG may be empty if the source has no explicit or inherited
10938 Check that all of the unmerged revisions in UNMERGED_CATALOG's
10939 mergeinfos are "phantoms", that is, one of the following conditions holds:
10941 1) The revision affects no corresponding paths in SOURCE_LOC.
10943 2) The revision affects corresponding paths in SOURCE_LOC,
10944 but based on the mergeinfo in MERGED_CATALOG, the change was
10947 Make a deep copy, allocated in RESULT_POOL, of any portions of
10948 UNMERGED_CATALOG that are not phantoms, to TRUE_UNMERGED_CATALOG.
10950 Note: The keys in all mergeinfo catalogs used here are relative to the
10951 root of the repository.
10953 RA_SESSION is an RA session open to the repository of TARGET_LOC; it may
10954 be temporarily reparented within this function.
10956 Use SCRATCH_POOL for all temporary allocations. */
10957 static svn_error_t *
10958 find_unsynced_ranges(const svn_client__pathrev_t *source_loc,
10959 const svn_client__pathrev_t *target_loc,
10960 svn_mergeinfo_catalog_t unmerged_catalog,
10961 svn_mergeinfo_catalog_t merged_catalog,
10962 svn_mergeinfo_catalog_t true_unmerged_catalog,
10963 svn_ra_session_t *ra_session,
10964 apr_pool_t *result_pool,
10965 apr_pool_t *scratch_pool)
10967 svn_rangelist_t *potentially_unmerged_ranges = NULL;
10969 /* Convert all the unmerged history to a rangelist. */
10970 if (apr_hash_count(unmerged_catalog))
10972 apr_hash_index_t *hi_catalog;
10974 potentially_unmerged_ranges =
10975 apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
10977 for (hi_catalog = apr_hash_first(scratch_pool, unmerged_catalog);
10979 hi_catalog = apr_hash_next(hi_catalog))
10981 svn_mergeinfo_t mergeinfo = apr_hash_this_val(hi_catalog);
10983 SVN_ERR(svn_rangelist__merge_many(potentially_unmerged_ranges,
10985 scratch_pool, scratch_pool));
10989 /* Find any unmerged revisions which both affect the source and
10990 are not yet merged to it. */
10991 if (potentially_unmerged_ranges)
10993 svn_revnum_t oldest_rev =
10994 (APR_ARRAY_IDX(potentially_unmerged_ranges,
10996 svn_merge_range_t *))->start + 1;
10997 svn_revnum_t youngest_rev =
10998 (APR_ARRAY_IDX(potentially_unmerged_ranges,
10999 potentially_unmerged_ranges->nelts - 1,
11000 svn_merge_range_t *))->end;
11001 log_find_operative_baton_t log_baton;
11002 const char *old_session_url = NULL;
11005 log_baton.merged_catalog = merged_catalog;
11006 log_baton.unmerged_catalog = true_unmerged_catalog;
11007 log_baton.source_repos_rel_path
11008 = svn_client__pathrev_relpath(source_loc, scratch_pool);
11009 log_baton.target_fspath
11010 = svn_client__pathrev_fspath(target_loc, scratch_pool);
11011 log_baton.result_pool = result_pool;
11013 /* Reparent the session to TARGET_LOC if this target location
11014 * exists within the unmerged revision range. */
11015 if (target_loc->rev <= youngest_rev && target_loc->rev >= oldest_rev)
11016 SVN_ERR(svn_client__ensure_ra_session_url(
11017 &old_session_url, ra_session, target_loc->url, scratch_pool));
11019 err = get_log(ra_session, "", youngest_rev, oldest_rev,
11020 TRUE, /* discover_changed_paths */
11021 log_find_operative_revs, &log_baton,
11023 if (old_session_url)
11024 err = svn_error_compose_create(err,
11025 svn_ra_reparent(ra_session,
11031 return SVN_NO_ERROR;
11035 /* Find the youngest revision that has been merged from target to source.
11037 * If any location in TARGET_HISTORY_AS_MERGEINFO is mentioned in
11038 * SOURCE_MERGEINFO, then we know that at least one merge was done from the
11039 * target to the source. In that case, set *YOUNGEST_MERGED_REV to the
11040 * youngest revision of that intersection (unless *YOUNGEST_MERGED_REV is
11041 * already younger than that). Otherwise, leave *YOUNGEST_MERGED_REV alone.
11043 static svn_error_t *
11044 find_youngest_merged_rev(svn_revnum_t *youngest_merged_rev,
11045 svn_mergeinfo_t target_history_as_mergeinfo,
11046 svn_mergeinfo_t source_mergeinfo,
11047 apr_pool_t *scratch_pool)
11049 svn_mergeinfo_t explicit_source_target_history_intersection;
11051 SVN_ERR(svn_mergeinfo_intersect2(
11052 &explicit_source_target_history_intersection,
11053 source_mergeinfo, target_history_as_mergeinfo, TRUE,
11054 scratch_pool, scratch_pool));
11055 if (apr_hash_count(explicit_source_target_history_intersection))
11057 svn_revnum_t old_rev, young_rev;
11059 /* Keep track of the youngest revision merged from target to source. */
11060 SVN_ERR(svn_mergeinfo__get_range_endpoints(
11061 &young_rev, &old_rev,
11062 explicit_source_target_history_intersection, scratch_pool));
11063 if (!SVN_IS_VALID_REVNUM(*youngest_merged_rev)
11064 || (young_rev > *youngest_merged_rev))
11065 *youngest_merged_rev = young_rev;
11068 return SVN_NO_ERROR;
11071 /* Set *FILTERED_MERGEINFO_P to the parts of TARGET_HISTORY_AS_MERGEINFO
11072 * that are not present in the source branch.
11074 * SOURCE_MERGEINFO is the explicit or inherited mergeinfo of the source
11075 * branch SOURCE_PATHREV. Extend SOURCE_MERGEINFO, modifying it in
11076 * place, to include the natural history (implicit mergeinfo) of
11077 * SOURCE_PATHREV. ### But make these additions in SCRATCH_POOL.
11079 * SOURCE_RA_SESSION is an RA session open to the repository containing
11080 * SOURCE_PATHREV; it may be temporarily reparented within this function.
11082 * ### [JAF] This function is named '..._subroutine' simply because I
11083 * factored it out based on code similarity, without knowing what it's
11084 * purpose is. We should clarify its purpose and choose a better name.
11086 static svn_error_t *
11087 find_unmerged_mergeinfo_subroutine(svn_mergeinfo_t *filtered_mergeinfo_p,
11088 svn_mergeinfo_t target_history_as_mergeinfo,
11089 svn_mergeinfo_t source_mergeinfo,
11090 const svn_client__pathrev_t *source_pathrev,
11091 svn_ra_session_t *source_ra_session,
11092 svn_client_ctx_t *ctx,
11093 apr_pool_t *result_pool,
11094 apr_pool_t *scratch_pool)
11096 svn_mergeinfo_t source_history_as_mergeinfo;
11098 /* Get the source path's natural history and merge it into source
11099 path's explicit or inherited mergeinfo. */
11100 SVN_ERR(svn_client__get_history_as_mergeinfo(
11101 &source_history_as_mergeinfo, NULL /* has_rev_zero_history */,
11102 source_pathrev, source_pathrev->rev, SVN_INVALID_REVNUM,
11103 source_ra_session, ctx, scratch_pool));
11104 SVN_ERR(svn_mergeinfo_merge2(source_mergeinfo,
11105 source_history_as_mergeinfo,
11106 scratch_pool, scratch_pool));
11108 /* Now source_mergeinfo represents everything we know about
11109 source_path's history. Now we need to know what part, if any, of the
11110 corresponding target's history is *not* part of source_path's total
11111 history; because it is neither shared history nor was it ever merged
11112 from the target to the source. */
11113 SVN_ERR(svn_mergeinfo_remove2(filtered_mergeinfo_p,
11115 target_history_as_mergeinfo, TRUE,
11116 result_pool, scratch_pool));
11117 return SVN_NO_ERROR;
11120 /* Helper for calculate_left_hand_side() which produces a mergeinfo catalog
11121 describing what parts of of the reintegrate target have not previously been
11122 merged to the reintegrate source.
11124 SOURCE_CATALOG is the collection of explicit mergeinfo on SOURCE_LOC and
11125 all its children, i.e. the mergeinfo catalog for the reintegrate source.
11127 TARGET_HISTORY_HASH is a hash of (const char *) paths mapped to
11128 svn_mergeinfo_t representing the location history. Each of these
11129 path keys represent a path in the reintegrate target, relative to the
11130 repository root, which has explicit mergeinfo and/or is the reintegrate
11131 target itself. The svn_mergeinfo_t's contain the natural history of each
11132 path@TARGET_REV. Effectively this is the mergeinfo catalog on the
11133 reintegrate target.
11135 YC_ANCESTOR_REV is the revision of the youngest common ancestor of the
11136 reintegrate source and the reintegrate target.
11138 SOURCE_LOC is the reintegrate source.
11140 SOURCE_RA_SESSION is a session opened to the URL of SOURCE_LOC
11141 and TARGET_RA_SESSION is open to TARGET->loc.url.
11143 For each entry in TARGET_HISTORY_HASH check that the history it
11144 represents is contained in either the explicit mergeinfo for the
11145 corresponding path in SOURCE_CATALOG, the corresponding path's inherited
11146 mergeinfo (if no explicit mergeinfo for the path is found in
11147 SOURCE_CATALOG), or the corresponding path's natural history. Populate
11148 *UNMERGED_TO_SOURCE_CATALOG with the corresponding source paths mapped to
11149 the mergeinfo from the target's natural history which is *not* found. Also
11150 include any mergeinfo from SOURCE_CATALOG which explicitly describes the
11151 target's history but for which *no* entry was found in
11152 TARGET_HISTORY_HASH.
11154 If no part of TARGET_HISTORY_HASH is found in SOURCE_CATALOG set
11155 *YOUNGEST_MERGED_REV to SVN_INVALID_REVNUM; otherwise set it to the youngest
11156 revision previously merged from the target to the source, and filter
11157 *UNMERGED_TO_SOURCE_CATALOG so that it contains no ranges greater than
11158 *YOUNGEST_MERGED_REV.
11160 *UNMERGED_TO_SOURCE_CATALOG is (deeply) allocated in RESULT_POOL.
11161 SCRATCH_POOL is used for all temporary allocations. */
11162 static svn_error_t *
11163 find_unmerged_mergeinfo(svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
11164 svn_revnum_t *youngest_merged_rev,
11165 svn_revnum_t yc_ancestor_rev,
11166 svn_mergeinfo_catalog_t source_catalog,
11167 apr_hash_t *target_history_hash,
11168 const svn_client__pathrev_t *source_loc,
11169 const merge_target_t *target,
11170 svn_ra_session_t *source_ra_session,
11171 svn_ra_session_t *target_ra_session,
11172 svn_client_ctx_t *ctx,
11173 apr_pool_t *result_pool,
11174 apr_pool_t *scratch_pool)
11176 const char *source_repos_rel_path
11177 = svn_client__pathrev_relpath(source_loc, scratch_pool);
11178 const char *target_repos_rel_path
11179 = svn_client__pathrev_relpath(&target->loc, scratch_pool);
11180 apr_hash_index_t *hi;
11181 svn_mergeinfo_catalog_t new_catalog = apr_hash_make(result_pool);
11182 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
11184 assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11185 assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11187 *youngest_merged_rev = SVN_INVALID_REVNUM;
11189 /* Examine the natural history of each path in the reintegrate target
11190 with explicit mergeinfo. */
11191 for (hi = apr_hash_first(scratch_pool, target_history_hash);
11193 hi = apr_hash_next(hi))
11195 const char *target_path = apr_hash_this_key(hi);
11196 svn_mergeinfo_t target_history_as_mergeinfo = apr_hash_this_val(hi);
11197 const char *path_rel_to_session
11198 = svn_relpath_skip_ancestor(target_repos_rel_path, target_path);
11199 const char *source_path;
11200 svn_client__pathrev_t *source_pathrev;
11201 svn_mergeinfo_t source_mergeinfo, filtered_mergeinfo;
11203 svn_pool_clear(iterpool);
11205 source_path = svn_relpath_join(source_repos_rel_path,
11206 path_rel_to_session, iterpool);
11207 source_pathrev = svn_client__pathrev_join_relpath(
11208 source_loc, path_rel_to_session, iterpool);
11210 /* Remove any target history that is also part of the source's history,
11211 i.e. their common ancestry. By definition this has already been
11212 "merged" from the target to the source. If the source has explicit
11213 self referential mergeinfo it would intersect with the target's
11214 history below, making it appear that some merges had been done from
11215 the target to the source, when this might not actually be the case. */
11216 SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
11217 &target_history_as_mergeinfo, target_history_as_mergeinfo,
11218 source_loc->rev, yc_ancestor_rev, TRUE, iterpool, iterpool));
11220 /* Look for any explicit mergeinfo on the source path corresponding to
11221 the target path. If we find any remove that from SOURCE_CATALOG.
11222 When this iteration over TARGET_HISTORY_HASH is complete all that
11223 should be left in SOURCE_CATALOG are subtrees that have explicit
11224 mergeinfo on the reintegrate source where there is no corresponding
11225 explicit mergeinfo on the reintegrate target. */
11226 source_mergeinfo = svn_hash_gets(source_catalog, source_path);
11227 if (source_mergeinfo)
11229 svn_hash_sets(source_catalog, source_path, NULL);
11231 SVN_ERR(find_youngest_merged_rev(youngest_merged_rev,
11232 target_history_as_mergeinfo,
11238 /* There is no mergeinfo on source_path *or* source_path doesn't
11239 exist at all. If simply doesn't exist we can ignore it
11241 svn_node_kind_t kind;
11243 SVN_ERR(svn_ra_check_path(source_ra_session,
11244 path_rel_to_session,
11245 source_loc->rev, &kind, iterpool));
11246 if (kind == svn_node_none)
11248 /* Else source_path does exist though it has no explicit mergeinfo.
11249 Find its inherited mergeinfo. If it doesn't have any then simply
11250 set source_mergeinfo to an empty hash. */
11251 SVN_ERR(svn_client__get_repos_mergeinfo(
11252 &source_mergeinfo, source_ra_session,
11253 source_pathrev->url, source_pathrev->rev,
11254 svn_mergeinfo_inherited, FALSE /*squelch_incapable*/,
11256 if (!source_mergeinfo)
11257 source_mergeinfo = apr_hash_make(iterpool);
11260 /* Use scratch_pool rather than iterpool because filtered_mergeinfo
11261 is going into new_catalog below and needs to last to the end of
11263 SVN_ERR(find_unmerged_mergeinfo_subroutine(
11264 &filtered_mergeinfo, target_history_as_mergeinfo,
11265 source_mergeinfo, source_pathrev,
11266 source_ra_session, ctx, scratch_pool, iterpool));
11267 svn_hash_sets(new_catalog, apr_pstrdup(scratch_pool, source_path),
11268 filtered_mergeinfo);
11271 /* Are there any subtrees with explicit mergeinfo still left in the merge
11272 source where there was no explicit mergeinfo for the corresponding path
11273 in the merge target? If so, add the intersection of those path's
11274 mergeinfo and the corresponding target path's mergeinfo to
11276 for (hi = apr_hash_first(scratch_pool, source_catalog);
11278 hi = apr_hash_next(hi))
11280 const char *source_path = apr_hash_this_key(hi);
11281 const char *path_rel_to_session =
11282 svn_relpath_skip_ancestor(source_repos_rel_path, source_path);
11283 const char *source_url;
11284 svn_mergeinfo_t source_mergeinfo = apr_hash_this_val(hi);
11285 svn_mergeinfo_t filtered_mergeinfo;
11286 svn_client__pathrev_t *target_pathrev;
11287 svn_mergeinfo_t target_history_as_mergeinfo;
11290 svn_pool_clear(iterpool);
11292 source_url = svn_path_url_add_component2(source_loc->url,
11293 path_rel_to_session, iterpool);
11294 target_pathrev = svn_client__pathrev_join_relpath(
11295 &target->loc, path_rel_to_session, iterpool);
11296 err = svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo,
11297 NULL /* has_rev_zero_history */,
11300 SVN_INVALID_REVNUM,
11305 if (err->apr_err == SVN_ERR_FS_NOT_FOUND
11306 || err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
11308 /* This path with explicit mergeinfo in the source doesn't
11309 exist on the target. */
11310 svn_error_clear(err);
11315 return svn_error_trace(err);
11320 svn_client__pathrev_t *pathrev;
11322 SVN_ERR(find_youngest_merged_rev(youngest_merged_rev,
11323 target_history_as_mergeinfo,
11327 /* Use scratch_pool rather than iterpool because filtered_mergeinfo
11328 is going into new_catalog below and needs to last to the end of
11330 /* ### Why looking at SOURCE_url at TARGET_rev? */
11331 SVN_ERR(svn_client__pathrev_create_with_session(
11332 &pathrev, source_ra_session, target->loc.rev, source_url,
11334 SVN_ERR(find_unmerged_mergeinfo_subroutine(
11335 &filtered_mergeinfo, target_history_as_mergeinfo,
11336 source_mergeinfo, pathrev,
11337 source_ra_session, ctx, scratch_pool, iterpool));
11338 if (apr_hash_count(filtered_mergeinfo))
11339 svn_hash_sets(new_catalog,
11340 apr_pstrdup(scratch_pool, source_path),
11341 filtered_mergeinfo);
11345 /* Limit new_catalog to the youngest revisions previously merged from
11346 the target to the source. */
11347 if (SVN_IS_VALID_REVNUM(*youngest_merged_rev))
11348 SVN_ERR(svn_mergeinfo__filter_catalog_by_ranges(&new_catalog,
11350 *youngest_merged_rev,
11351 0, /* No oldest bound. */
11356 /* Make a shiny new copy before blowing away all the temporary pools. */
11357 *unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(new_catalog,
11359 svn_pool_destroy(iterpool);
11360 return SVN_NO_ERROR;
11363 /* Helper for svn_client_merge_reintegrate() which calculates the
11364 'left hand side' of the underlying two-URL merge that a --reintegrate
11365 merge actually performs. If no merge should be performed, set
11368 TARGET->abspath is the absolute working copy path of the reintegrate
11371 SOURCE_LOC is the reintegrate source.
11373 SUBTREES_WITH_MERGEINFO is a hash of (const char *) absolute paths mapped
11374 to (svn_mergeinfo_t *) mergeinfo values for each working copy path with
11375 explicit mergeinfo in TARGET->abspath. Actually we only need to know the
11376 paths, not the mergeinfo.
11378 TARGET->loc.rev is the working revision the entire WC tree rooted at
11381 Populate *UNMERGED_TO_SOURCE_CATALOG with the mergeinfo describing what
11382 parts of TARGET->loc have not been merged to SOURCE_LOC, up to the
11383 youngest revision ever merged from the TARGET->abspath to the source if
11384 such exists, see doc string for find_unmerged_mergeinfo().
11386 SOURCE_RA_SESSION is a session opened to the SOURCE_LOC
11387 and TARGET_RA_SESSION is open to TARGET->loc.url.
11389 *LEFT_P, *MERGED_TO_SOURCE_CATALOG , and *UNMERGED_TO_SOURCE_CATALOG are
11390 allocated in RESULT_POOL. SCRATCH_POOL is used for all temporary
11392 static svn_error_t *
11393 calculate_left_hand_side(svn_client__pathrev_t **left_p,
11394 svn_mergeinfo_catalog_t *merged_to_source_catalog,
11395 svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
11396 const merge_target_t *target,
11397 apr_hash_t *subtrees_with_mergeinfo,
11398 const svn_client__pathrev_t *source_loc,
11399 svn_ra_session_t *source_ra_session,
11400 svn_ra_session_t *target_ra_session,
11401 svn_client_ctx_t *ctx,
11402 apr_pool_t *result_pool,
11403 apr_pool_t *scratch_pool)
11405 svn_mergeinfo_catalog_t mergeinfo_catalog, unmerged_catalog;
11406 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
11407 apr_hash_index_t *hi;
11408 /* hash of paths mapped to arrays of svn_mergeinfo_t. */
11409 apr_hash_t *target_history_hash = apr_hash_make(scratch_pool);
11410 svn_revnum_t youngest_merged_rev;
11411 svn_client__pathrev_t *yc_ancestor;
11413 assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11414 assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11416 /* Initialize our return variables. */
11419 /* TARGET->abspath may not have explicit mergeinfo and thus may not be
11420 contained within SUBTREES_WITH_MERGEINFO. If this is the case then
11421 add a dummy item for TARGET->abspath so we get its history (i.e. implicit
11422 mergeinfo) below. */
11423 if (!svn_hash_gets(subtrees_with_mergeinfo, target->abspath))
11424 svn_hash_sets(subtrees_with_mergeinfo, target->abspath,
11425 apr_hash_make(result_pool));
11427 /* Get the history segments (as mergeinfo) for TARGET->abspath and any of
11428 its subtrees with explicit mergeinfo. */
11429 for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
11431 hi = apr_hash_next(hi))
11433 const char *local_abspath = apr_hash_this_key(hi);
11434 svn_client__pathrev_t *target_child;
11435 const char *repos_relpath;
11436 svn_mergeinfo_t target_history_as_mergeinfo;
11438 svn_pool_clear(iterpool);
11440 /* Convert the absolute path with mergeinfo on it to a path relative
11441 to the session root. */
11442 SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, NULL, NULL,
11443 ctx->wc_ctx, local_abspath,
11444 scratch_pool, iterpool));
11445 target_child = svn_client__pathrev_create_with_relpath(
11446 target->loc.repos_root_url, target->loc.repos_uuid,
11447 target->loc.rev, repos_relpath, iterpool);
11448 SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo,
11449 NULL /* has_rev_zero_hist */,
11452 SVN_INVALID_REVNUM,
11454 ctx, scratch_pool));
11456 svn_hash_sets(target_history_hash, repos_relpath,
11457 target_history_as_mergeinfo);
11460 /* Check that SOURCE_LOC and TARGET->loc are
11461 actually related, we can't reintegrate if they are not. Also
11462 get an initial value for the YCA revision number. */
11463 SVN_ERR(svn_client__get_youngest_common_ancestor(
11464 &yc_ancestor, source_loc, &target->loc, target_ra_session, ctx,
11465 iterpool, iterpool));
11467 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11468 _("'%s@%ld' must be ancestrally related to "
11469 "'%s@%ld'"), source_loc->url, source_loc->rev,
11470 target->loc.url, target->loc.rev);
11472 /* If the source revision is the same as the youngest common
11473 revision, then there can't possibly be any unmerged revisions
11474 that we need to apply to target. */
11475 if (source_loc->rev == yc_ancestor->rev)
11477 svn_pool_destroy(iterpool);
11478 return SVN_NO_ERROR;
11481 /* Get the mergeinfo from the source, including its descendants
11482 with differing explicit mergeinfo. */
11483 SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
11484 &mergeinfo_catalog, source_ra_session,
11485 source_loc->url, source_loc->rev,
11486 svn_mergeinfo_inherited, FALSE /* squelch_incapable */,
11487 TRUE /* include_descendants */, iterpool, iterpool));
11489 if (!mergeinfo_catalog)
11490 mergeinfo_catalog = apr_hash_make(iterpool);
11492 *merged_to_source_catalog = svn_mergeinfo_catalog_dup(mergeinfo_catalog,
11495 /* Filter the source's mergeinfo catalog so that we are left with
11496 mergeinfo that describes what has *not* previously been merged from
11497 TARGET->loc to SOURCE_LOC. */
11498 SVN_ERR(find_unmerged_mergeinfo(&unmerged_catalog,
11499 &youngest_merged_rev,
11502 target_history_hash,
11508 iterpool, iterpool));
11510 /* Simplify unmerged_catalog through elision then make a copy in POOL. */
11511 SVN_ERR(svn_client__elide_mergeinfo_catalog(unmerged_catalog,
11513 *unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(unmerged_catalog,
11516 if (youngest_merged_rev == SVN_INVALID_REVNUM)
11518 /* We never merged to the source. Just return the branch point. */
11519 *left_p = svn_client__pathrev_dup(yc_ancestor, result_pool);
11523 /* We've previously merged some or all of the target, up to
11524 youngest_merged_rev, to the source. Set
11525 *LEFT_P to cover the youngest part of this range. */
11526 SVN_ERR(svn_client__repos_location(left_p, target_ra_session,
11527 &target->loc, youngest_merged_rev,
11528 ctx, result_pool, iterpool));
11531 svn_pool_destroy(iterpool);
11532 return SVN_NO_ERROR;
11535 /* Determine the URLs and revisions needed to perform a reintegrate merge
11536 * from SOURCE_LOC into the working copy at TARGET.
11538 * SOURCE_RA_SESSION and TARGET_RA_SESSION are RA sessions opened to the
11539 * URLs of SOURCE_LOC and TARGET->loc respectively.
11542 * the source-left and source-right locations of the required merge. Set
11543 * *YC_ANCESTOR_P to the location of the youngest ancestor.
11544 * Any of these output pointers may be NULL if not wanted.
11546 * See svn_client_find_reintegrate_merge() for other details.
11548 static svn_error_t *
11549 find_reintegrate_merge(merge_source_t **source_p,
11550 svn_client__pathrev_t **yc_ancestor_p,
11551 svn_ra_session_t *source_ra_session,
11552 const svn_client__pathrev_t *source_loc,
11553 svn_ra_session_t *target_ra_session,
11554 const merge_target_t *target,
11555 svn_client_ctx_t *ctx,
11556 apr_pool_t *result_pool,
11557 apr_pool_t *scratch_pool)
11559 svn_client__pathrev_t *yc_ancestor;
11560 svn_client__pathrev_t *loc1;
11561 merge_source_t source;
11562 svn_mergeinfo_catalog_t unmerged_to_source_mergeinfo_catalog;
11563 svn_mergeinfo_catalog_t merged_to_source_mergeinfo_catalog;
11565 apr_hash_t *subtrees_with_mergeinfo;
11567 assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11568 assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11570 /* As the WC tree is "pure", use its last-updated-to revision as
11571 the default revision for the left side of our merge, since that's
11572 what the repository sub-tree is required to be up to date with
11573 (with regard to the WC). */
11574 /* ### Bogus/obsolete comment? */
11576 /* Can't reintegrate to or from the root of the repository. */
11577 if (strcmp(source_loc->url, source_loc->repos_root_url) == 0
11578 || strcmp(target->loc.url, target->loc.repos_root_url) == 0)
11579 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11580 _("Neither the reintegrate source nor target "
11581 "can be the root of the repository"));
11583 /* Find all the subtrees in TARGET_WCPATH that have explicit mergeinfo. */
11584 err = get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
11585 target->abspath, svn_depth_infinity,
11586 ctx, scratch_pool, scratch_pool);
11587 /* Issue #3896: If invalid mergeinfo in the reintegrate target
11588 prevents us from proceeding, then raise the best error possible. */
11589 if (err && err->apr_err == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
11590 err = svn_error_quick_wrap(err, _("Reintegrate merge not possible"));
11593 SVN_ERR(calculate_left_hand_side(&loc1,
11594 &merged_to_source_mergeinfo_catalog,
11595 &unmerged_to_source_mergeinfo_catalog,
11597 subtrees_with_mergeinfo,
11602 scratch_pool, scratch_pool));
11604 /* Did calculate_left_hand_side() decide that there was no merge to
11605 be performed here? */
11611 *yc_ancestor_p = NULL;
11612 return SVN_NO_ERROR;
11615 source.loc1 = loc1;
11616 source.loc2 = source_loc;
11618 /* If the target was moved after the source was branched from it,
11619 it is possible that the left URL differs from the target's current
11620 URL. If so, then adjust TARGET_RA_SESSION to point to the old URL. */
11621 if (strcmp(source.loc1->url, target->loc.url))
11622 SVN_ERR(svn_ra_reparent(target_ra_session, source.loc1->url, scratch_pool));
11624 SVN_ERR(svn_client__get_youngest_common_ancestor(
11625 &yc_ancestor, source.loc2, source.loc1, target_ra_session,
11626 ctx, scratch_pool, scratch_pool));
11629 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11630 _("'%s@%ld' must be ancestrally related to "
11632 source.loc1->url, source.loc1->rev,
11633 source.loc2->url, source.loc2->rev);
11635 /* The source side of a reintegrate merge is not 'ancestral', except in
11636 * the degenerate case where source == YCA. */
11637 source.ancestral = (loc1->rev == yc_ancestor->rev);
11639 if (source.loc1->rev > yc_ancestor->rev)
11641 /* Have we actually merged anything to the source from the
11642 target? If so, make sure we've merged a contiguous
11644 svn_mergeinfo_catalog_t final_unmerged_catalog = apr_hash_make(scratch_pool);
11646 SVN_ERR(find_unsynced_ranges(source_loc, &target->loc,
11647 unmerged_to_source_mergeinfo_catalog,
11648 merged_to_source_mergeinfo_catalog,
11649 final_unmerged_catalog,
11650 target_ra_session, scratch_pool,
11653 if (apr_hash_count(final_unmerged_catalog))
11655 svn_string_t *source_mergeinfo_cat_string;
11657 SVN_ERR(svn_mergeinfo__catalog_to_formatted_string(
11658 &source_mergeinfo_cat_string,
11659 final_unmerged_catalog,
11660 " ", _(" Missing ranges: "), scratch_pool));
11661 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
11663 _("Reintegrate can only be used if "
11664 "revisions %ld through %ld were "
11665 "previously merged from %s to the "
11666 "reintegrate source, but this is "
11667 "not the case:\n%s"),
11668 yc_ancestor->rev + 1, source.loc2->rev,
11670 source_mergeinfo_cat_string->data);
11674 /* Left side: trunk@youngest-trunk-rev-merged-to-branch-at-specified-peg-rev
11675 * Right side: branch@specified-peg-revision */
11677 *source_p = merge_source_dup(&source, result_pool);
11680 *yc_ancestor_p = svn_client__pathrev_dup(yc_ancestor, result_pool);
11681 return SVN_NO_ERROR;
11684 /* Resolve the source and target locations and open RA sessions to them, and
11685 * perform some checks appropriate for a reintegrate merge.
11687 * Set *SOURCE_RA_SESSION_P and *SOURCE_LOC_P to a new session and the
11688 * repository location of SOURCE_PATH_OR_URL at SOURCE_PEG_REVISION. Set
11689 * *TARGET_RA_SESSION_P and *TARGET_P to a new session and the repository
11690 * location of the WC at TARGET_ABSPATH.
11692 * Throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES error if the target WC node is
11693 * a locally added node or if the source and target are not in the same
11694 * repository. Throw a SVN_ERR_CLIENT_NOT_READY_TO_MERGE error if the
11695 * target WC is not at a single revision without switched subtrees and
11696 * without local mods.
11698 * Allocate all the outputs in RESULT_POOL.
11700 static svn_error_t *
11701 open_reintegrate_source_and_target(svn_ra_session_t **source_ra_session_p,
11702 svn_client__pathrev_t **source_loc_p,
11703 svn_ra_session_t **target_ra_session_p,
11704 merge_target_t **target_p,
11705 const char *source_path_or_url,
11706 const svn_opt_revision_t *source_peg_revision,
11707 const char *target_abspath,
11708 svn_client_ctx_t *ctx,
11709 apr_pool_t *result_pool,
11710 apr_pool_t *scratch_pool)
11712 svn_client__pathrev_t *source_loc;
11713 merge_target_t *target;
11715 /* Open the target WC. A reintegrate merge requires the merge target to
11716 * reflect a subtree of the repository as found at a single revision. */
11717 SVN_ERR(open_target_wc(&target, target_abspath,
11718 FALSE, FALSE, FALSE,
11719 ctx, scratch_pool, scratch_pool));
11720 if (! target->loc.url)
11721 return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
11722 _("Can't reintegrate into '%s' because it is "
11723 "locally added and therefore not related to "
11724 "the merge source"),
11725 svn_dirent_local_style(target->abspath,
11728 SVN_ERR(svn_client_open_ra_session2(target_ra_session_p,
11729 target->loc.url, target->abspath,
11730 ctx, result_pool, scratch_pool));
11732 SVN_ERR(svn_client__ra_session_from_path2(
11733 source_ra_session_p, &source_loc,
11734 source_path_or_url, NULL, source_peg_revision, source_peg_revision,
11735 ctx, result_pool));
11737 /* source_loc and target->loc are required to be in the same repository,
11738 as mergeinfo doesn't come into play for cross-repository merging. */
11739 SVN_ERR(check_same_repos(source_loc,
11740 svn_dirent_local_style(source_path_or_url,
11743 svn_dirent_local_style(target->abspath,
11745 TRUE /* strict_urls */, scratch_pool));
11747 *source_loc_p = source_loc;
11748 *target_p = target;
11749 return SVN_NO_ERROR;
11752 /* The body of svn_client_merge_reintegrate(), which see for details. */
11753 static svn_error_t *
11754 merge_reintegrate_locked(conflict_report_t **conflict_report,
11755 const char *source_path_or_url,
11756 const svn_opt_revision_t *source_peg_revision,
11757 const char *target_abspath,
11758 svn_boolean_t diff_ignore_ancestry,
11759 svn_boolean_t dry_run,
11760 const apr_array_header_t *merge_options,
11761 svn_client_ctx_t *ctx,
11762 apr_pool_t *result_pool,
11763 apr_pool_t *scratch_pool)
11765 svn_ra_session_t *target_ra_session, *source_ra_session;
11766 merge_target_t *target;
11767 svn_client__pathrev_t *source_loc;
11768 merge_source_t *source;
11769 svn_client__pathrev_t *yc_ancestor;
11770 svn_boolean_t use_sleep = FALSE;
11773 SVN_ERR(open_reintegrate_source_and_target(
11774 &source_ra_session, &source_loc, &target_ra_session, &target,
11775 source_path_or_url, source_peg_revision, target_abspath,
11776 ctx, scratch_pool, scratch_pool));
11778 SVN_ERR(find_reintegrate_merge(&source, &yc_ancestor,
11779 source_ra_session, source_loc,
11780 target_ra_session, target,
11781 ctx, scratch_pool, scratch_pool));
11785 *conflict_report = NULL;
11786 return SVN_NO_ERROR;
11789 /* Do the real merge! */
11790 /* ### TODO(reint): Make sure that one isn't the same line ancestor
11791 ### of the other (what's erroneously referred to as "ancestrally
11792 ### related" in this source file). For now, we just say the source
11793 ### isn't "ancestral" even if it is (in the degenerate case where
11794 ### source-left equals YCA). */
11795 source->ancestral = FALSE;
11796 err = merge_cousins_and_supplement_mergeinfo(conflict_report,
11801 source, yc_ancestor,
11802 TRUE /* same_repos */,
11803 svn_depth_infinity,
11804 diff_ignore_ancestry,
11805 FALSE /* force_delete */,
11806 FALSE /* record_only */,
11810 result_pool, scratch_pool);
11813 svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
11816 return SVN_NO_ERROR;
11820 svn_client_merge_reintegrate(const char *source_path_or_url,
11821 const svn_opt_revision_t *source_peg_revision,
11822 const char *target_wcpath,
11823 svn_boolean_t dry_run,
11824 const apr_array_header_t *merge_options,
11825 svn_client_ctx_t *ctx,
11828 const char *target_abspath, *lock_abspath;
11829 conflict_report_t *conflict_report;
11831 SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
11832 target_wcpath, ctx, pool));
11835 SVN_WC__CALL_WITH_WRITE_LOCK(
11836 merge_reintegrate_locked(&conflict_report,
11837 source_path_or_url, source_peg_revision,
11839 FALSE /*diff_ignore_ancestry*/,
11840 dry_run, merge_options, ctx, pool, pool),
11841 ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
11843 SVN_ERR(merge_reintegrate_locked(&conflict_report,
11844 source_path_or_url, source_peg_revision,
11846 FALSE /*diff_ignore_ancestry*/,
11847 dry_run, merge_options, ctx, pool, pool));
11849 SVN_ERR(make_merge_conflict_error(conflict_report, pool));
11850 return SVN_NO_ERROR;
11854 /* The body of svn_client_merge_peg5(), which see for details.
11856 * IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge().
11858 static svn_error_t *
11859 merge_peg_locked(conflict_report_t **conflict_report,
11860 const char *source_path_or_url,
11861 const svn_opt_revision_t *source_peg_revision,
11862 const svn_rangelist_t *ranges_to_merge,
11863 const char *target_abspath,
11865 svn_boolean_t ignore_mergeinfo,
11866 svn_boolean_t diff_ignore_ancestry,
11867 svn_boolean_t force_delete,
11868 svn_boolean_t record_only,
11869 svn_boolean_t dry_run,
11870 svn_boolean_t allow_mixed_rev,
11871 const apr_array_header_t *merge_options,
11872 svn_client_ctx_t *ctx,
11873 apr_pool_t *result_pool,
11874 apr_pool_t *scratch_pool)
11876 merge_target_t *target;
11877 svn_client__pathrev_t *source_loc;
11878 apr_array_header_t *merge_sources;
11879 svn_ra_session_t *ra_session;
11880 apr_pool_t *sesspool;
11881 svn_boolean_t use_sleep = FALSE;
11883 svn_boolean_t same_repos;
11885 SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
11887 SVN_ERR(open_target_wc(&target, target_abspath,
11888 allow_mixed_rev, TRUE, TRUE,
11889 ctx, scratch_pool, scratch_pool));
11891 /* Create a short lived session pool */
11892 sesspool = svn_pool_create(scratch_pool);
11894 /* Open an RA session to our source URL, and determine its root URL. */
11895 SVN_ERR(svn_client__ra_session_from_path2(
11896 &ra_session, &source_loc,
11897 source_path_or_url, NULL, source_peg_revision, source_peg_revision,
11900 /* Normalize our merge sources. */
11901 SVN_ERR(normalize_merge_sources(&merge_sources, source_path_or_url,
11903 ranges_to_merge, ra_session, ctx,
11904 scratch_pool, scratch_pool));
11906 /* Check for same_repos. */
11907 same_repos = is_same_repos(&target->loc, source_loc, TRUE /* strict_urls */);
11909 /* Do the real merge! (We say with confidence that our merge
11910 sources are both ancestral and related.) */
11911 err = do_merge(NULL, NULL, conflict_report, &use_sleep,
11912 merge_sources, target, ra_session,
11913 TRUE /*sources_related*/, same_repos, ignore_mergeinfo,
11914 diff_ignore_ancestry, force_delete, dry_run,
11915 record_only, NULL, FALSE, FALSE, depth, merge_options,
11916 ctx, result_pool, scratch_pool);
11918 /* We're done with our RA session. */
11919 svn_pool_destroy(sesspool);
11922 svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
11925 return SVN_NO_ERROR;
11928 /* Details of an automatic merge. */
11929 typedef struct automatic_merge_t
11931 svn_client__pathrev_t *yca, *base, *right, *target;
11932 svn_boolean_t is_reintegrate_like;
11933 svn_boolean_t allow_mixed_rev, allow_local_mods, allow_switched_subtrees;
11934 } automatic_merge_t;
11936 static svn_error_t *
11937 client_find_automatic_merge(automatic_merge_t **merge_p,
11938 const char *source_path_or_url,
11939 const svn_opt_revision_t *source_revision,
11940 const char *target_abspath,
11941 svn_boolean_t allow_mixed_rev,
11942 svn_boolean_t allow_local_mods,
11943 svn_boolean_t allow_switched_subtrees,
11944 svn_client_ctx_t *ctx,
11945 apr_pool_t *result_pool,
11946 apr_pool_t *scratch_pool);
11948 static svn_error_t *
11949 do_automatic_merge_locked(conflict_report_t **conflict_report,
11950 const automatic_merge_t *merge,
11951 const char *target_abspath,
11953 svn_boolean_t diff_ignore_ancestry,
11954 svn_boolean_t force_delete,
11955 svn_boolean_t record_only,
11956 svn_boolean_t dry_run,
11957 const apr_array_header_t *merge_options,
11958 svn_client_ctx_t *ctx,
11959 apr_pool_t *result_pool,
11960 apr_pool_t *scratch_pool);
11963 svn_client_merge_peg5(const char *source_path_or_url,
11964 const apr_array_header_t *ranges_to_merge,
11965 const svn_opt_revision_t *source_peg_revision,
11966 const char *target_wcpath,
11968 svn_boolean_t ignore_mergeinfo,
11969 svn_boolean_t diff_ignore_ancestry,
11970 svn_boolean_t force_delete,
11971 svn_boolean_t record_only,
11972 svn_boolean_t dry_run,
11973 svn_boolean_t allow_mixed_rev,
11974 const apr_array_header_t *merge_options,
11975 svn_client_ctx_t *ctx,
11978 const char *target_abspath, *lock_abspath;
11979 conflict_report_t *conflict_report;
11981 /* No ranges to merge? No problem. */
11982 if (ranges_to_merge != NULL && ranges_to_merge->nelts == 0)
11983 return SVN_NO_ERROR;
11985 SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
11986 target_wcpath, ctx, pool));
11988 /* Do an automatic merge if no revision ranges are specified. */
11989 if (ranges_to_merge == NULL)
11991 automatic_merge_t *merge;
11993 if (ignore_mergeinfo)
11994 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
11995 _("Cannot merge automatically while "
11996 "ignoring mergeinfo"));
11998 /* Find the details of the merge needed. */
11999 SVN_ERR(client_find_automatic_merge(
12001 source_path_or_url, source_peg_revision,
12004 TRUE /*allow_local_mods*/,
12005 TRUE /*allow_switched_subtrees*/,
12009 SVN_WC__CALL_WITH_WRITE_LOCK(
12010 do_automatic_merge_locked(&conflict_report,
12012 target_abspath, depth,
12013 diff_ignore_ancestry,
12014 force_delete, record_only, dry_run,
12015 merge_options, ctx, pool, pool),
12016 ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
12018 SVN_ERR(do_automatic_merge_locked(&conflict_report,
12020 target_abspath, depth,
12021 diff_ignore_ancestry,
12022 force_delete, record_only, dry_run,
12023 merge_options, ctx, pool, pool));
12026 SVN_WC__CALL_WITH_WRITE_LOCK(
12027 merge_peg_locked(&conflict_report,
12028 source_path_or_url, source_peg_revision,
12030 target_abspath, depth, ignore_mergeinfo,
12031 diff_ignore_ancestry,
12032 force_delete, record_only, dry_run,
12033 allow_mixed_rev, merge_options, ctx, pool, pool),
12034 ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
12036 SVN_ERR(merge_peg_locked(&conflict_report,
12037 source_path_or_url, source_peg_revision,
12039 target_abspath, depth, ignore_mergeinfo,
12040 diff_ignore_ancestry,
12041 force_delete, record_only, dry_run,
12042 allow_mixed_rev, merge_options, ctx, pool, pool));
12044 SVN_ERR(make_merge_conflict_error(conflict_report, pool));
12045 return SVN_NO_ERROR;
12049 /* The location-history of a branch.
12051 * This structure holds the set of path-revisions occupied by a branch,
12052 * from an externally chosen 'tip' location back to its origin. The
12053 * 'tip' location is the youngest location that we are considering on
12055 typedef struct branch_history_t
12057 /* The tip location of the branch. That is, the youngest location that's
12058 * in the repository and that we're considering. If we're considering a
12059 * target branch right up to an uncommitted WC, then this is the WC base
12060 * (pristine) location. */
12061 svn_client__pathrev_t *tip;
12062 /* The location-segment history, as mergeinfo. */
12063 svn_mergeinfo_t history;
12064 /* Whether the location-segment history reached as far as (necessarily
12065 the root path in) revision 0 -- a fact that can't be represented as
12067 svn_boolean_t has_r0_history;
12068 } branch_history_t;
12070 /* Return the location on BRANCH_HISTORY at revision REV, or NULL if none. */
12071 static svn_client__pathrev_t *
12072 location_on_branch_at_rev(const branch_history_t *branch_history,
12074 apr_pool_t *result_pool,
12075 apr_pool_t *scratch_pool)
12077 apr_hash_index_t *hi;
12079 for (hi = apr_hash_first(scratch_pool, branch_history->history); hi;
12080 hi = apr_hash_next(hi))
12082 const char *fspath = apr_hash_this_key(hi);
12083 svn_rangelist_t *rangelist = apr_hash_this_val(hi);
12086 for (i = 0; i < rangelist->nelts; i++)
12088 svn_merge_range_t *r = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
12089 if (r->start < rev && rev <= r->end)
12091 return svn_client__pathrev_create_with_relpath(
12092 branch_history->tip->repos_root_url,
12093 branch_history->tip->repos_uuid,
12094 rev, fspath + 1, result_pool);
12102 typedef struct source_and_target_t
12104 svn_client__pathrev_t *source;
12105 svn_ra_session_t *source_ra_session;
12106 branch_history_t source_branch;
12108 merge_target_t *target;
12109 svn_ra_session_t *target_ra_session;
12110 branch_history_t target_branch;
12112 /* Repos location of the youngest common ancestor of SOURCE and TARGET. */
12113 svn_client__pathrev_t *yca;
12114 } source_and_target_t;
12116 /* Set *INTERSECTION_P to the intersection of BRANCH_HISTORY with the
12117 * revision range OLDEST_REV to YOUNGEST_REV (inclusive).
12119 * If the intersection is empty, the result will be a branch history object
12120 * containing an empty (not null) history.
12122 * ### The 'tip' of the result is currently unchanged.
12124 static svn_error_t *
12125 branch_history_intersect_range(branch_history_t **intersection_p,
12126 const branch_history_t *branch_history,
12127 svn_revnum_t oldest_rev,
12128 svn_revnum_t youngest_rev,
12129 apr_pool_t *result_pool,
12130 apr_pool_t *scratch_pool)
12132 branch_history_t *result = apr_palloc(result_pool, sizeof(*result));
12134 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
12135 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
12136 SVN_ERR_ASSERT(oldest_rev >= 1);
12137 /* Allow a just-empty range (oldest = youngest + 1) but not an
12138 * arbitrary reverse range (such as oldest = youngest + 2). */
12139 SVN_ERR_ASSERT(oldest_rev <= youngest_rev + 1);
12141 if (oldest_rev <= youngest_rev)
12143 SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
12144 &result->history, branch_history->history,
12145 youngest_rev, oldest_rev - 1, TRUE /* include_range */,
12146 result_pool, scratch_pool));
12147 result->history = svn_mergeinfo_dup(result->history, result_pool);
12151 result->history = apr_hash_make(result_pool);
12153 result->has_r0_history = FALSE;
12155 /* ### TODO: Set RESULT->tip to the tip of the intersection. */
12156 result->tip = svn_client__pathrev_dup(branch_history->tip, result_pool);
12158 *intersection_p = result;
12159 return SVN_NO_ERROR;
12162 /* Set *OLDEST_P and *YOUNGEST_P to the oldest and youngest locations
12163 * (inclusive) along BRANCH. OLDEST_P and/or YOUNGEST_P may be NULL if not
12166 static svn_error_t *
12167 branch_history_get_endpoints(svn_client__pathrev_t **oldest_p,
12168 svn_client__pathrev_t **youngest_p,
12169 const branch_history_t *branch,
12170 apr_pool_t *result_pool,
12171 apr_pool_t *scratch_pool)
12173 svn_revnum_t youngest_rev, oldest_rev;
12175 SVN_ERR(svn_mergeinfo__get_range_endpoints(
12176 &youngest_rev, &oldest_rev,
12177 branch->history, scratch_pool));
12179 *oldest_p = location_on_branch_at_rev(
12180 branch, oldest_rev + 1, result_pool, scratch_pool);
12182 *youngest_p = location_on_branch_at_rev(
12183 branch, youngest_rev, result_pool, scratch_pool);
12184 return SVN_NO_ERROR;
12187 /* Implements the svn_log_entry_receiver_t interface.
12189 Set *BATON to LOG_ENTRY->revision and return SVN_ERR_CEASE_INVOCATION. */
12190 static svn_error_t *
12191 operative_rev_receiver(void *baton,
12192 svn_log_entry_t *log_entry,
12195 svn_revnum_t *operative_rev = baton;
12197 *operative_rev = log_entry->revision;
12199 /* We've found the youngest merged or oldest eligible revision, so
12202 ...but wait, shouldn't we care if LOG_ENTRY->NON_INHERITABLE is
12203 true? Because if it is, then LOG_ENTRY->REVISION is only
12204 partially merged/elgibile! And our only caller,
12205 find_last_merged_location (via short_circuit_mergeinfo_log) is
12206 interested in *fully* merged revisions. That's all true, but if
12207 find_last_merged_location() finds the youngest merged revision it
12208 will also check for the oldest eligible revision. So in the case
12209 the youngest merged rev is non-inheritable, the *same* non-inheritable
12210 rev will be found as the oldest eligible rev -- and
12211 find_last_merged_location() handles that situation. */
12212 return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
12215 /* Wrapper around svn_client__mergeinfo_log. All arguments are as per
12216 that private API. The discover_changed_paths, depth, and revprops args to
12217 svn_client__mergeinfo_log are always TRUE, svn_depth_infinity_t,
12218 and empty array respectively.
12220 If RECEIVER raises a SVN_ERR_CEASE_INVOCATION error, but still sets
12221 *REVISION to a valid revnum, then clear the error. Otherwise return
12223 static svn_error_t*
12224 short_circuit_mergeinfo_log(svn_mergeinfo_catalog_t *target_mergeinfo_cat,
12225 svn_boolean_t finding_merged,
12226 const char *target_path_or_url,
12227 const svn_opt_revision_t *target_peg_revision,
12228 const char *source_path_or_url,
12229 const svn_opt_revision_t *source_peg_revision,
12230 const svn_opt_revision_t *source_start_revision,
12231 const svn_opt_revision_t *source_end_revision,
12232 svn_log_entry_receiver_t receiver,
12233 svn_revnum_t *revision,
12234 svn_client_ctx_t *ctx,
12235 svn_ra_session_t *ra_session,
12236 apr_pool_t *result_pool,
12237 apr_pool_t *scratch_pool)
12239 apr_array_header_t *revprops;
12241 const char *session_url;
12243 SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
12245 revprops = apr_array_make(scratch_pool, 0, sizeof(const char *));
12246 err = svn_client__mergeinfo_log(finding_merged,
12247 target_path_or_url,
12248 target_peg_revision,
12249 target_mergeinfo_cat,
12250 source_path_or_url,
12251 source_peg_revision,
12252 source_start_revision,
12253 source_end_revision,
12254 receiver, revision,
12255 TRUE, svn_depth_infinity,
12256 revprops, ctx, ra_session,
12257 result_pool, scratch_pool);
12259 err = svn_error_compose_create(
12261 svn_ra_reparent(ra_session, session_url, scratch_pool));
12265 /* We expect RECEIVER to short-circuit the (potentially expensive) log
12266 by raising an SVN_ERR_CEASE_INVOCATION -- see operative_rev_receiver.
12267 So we can ignore that error, but only as long as we actually found a
12269 if (SVN_IS_VALID_REVNUM(*revision)
12270 && err->apr_err == SVN_ERR_CEASE_INVOCATION)
12272 svn_error_clear(err);
12277 return svn_error_trace(err);
12280 return SVN_NO_ERROR;
12283 /* Set *BASE_P to the last location on SOURCE_BRANCH such that all changes
12284 * on SOURCE_BRANCH after YCA up to and including *BASE_P have already
12285 * been fully merged into TARGET.
12288 * o-------o-----------o--- SOURCE_BRANCH
12292 * o-----------o----------- TARGET branch
12294 * In terms of mergeinfo:
12296 * Source a--... o=change, -=no-op revision
12298 * YCA --> o a---o---o---o---o--- d=delete, a=add-as-a-copy
12300 * Eligible -.eee.eeeeeeeeeeeeeeeeeeee .=not a source branch location
12302 * Tgt-mi -.mmm.mm-mm-------m------- m=merged to root of TARGET or
12303 * subtree of TARGET with no
12304 * operative changes outside of that
12305 * subtree, -=not merged
12307 * Eligible -.---.--e--eeeeeee-eeeeeee
12309 * Next --------^----------------- BASE is just before here.
12314 * o-----------o-------------
12316 * If no revisions from SOURCE_BRANCH have been completely merged to TARGET,
12317 * then set *BASE_P to the YCA.
12319 static svn_error_t *
12320 find_last_merged_location(svn_client__pathrev_t **base_p,
12321 svn_client__pathrev_t *yca,
12322 const branch_history_t *source_branch,
12323 svn_client__pathrev_t *target,
12324 svn_client_ctx_t *ctx,
12325 svn_ra_session_t *ra_session,
12326 apr_pool_t *result_pool,
12327 apr_pool_t *scratch_pool)
12329 svn_opt_revision_t source_peg_rev, source_start_rev, source_end_rev,
12331 svn_revnum_t youngest_merged_rev = SVN_INVALID_REVNUM;
12332 svn_mergeinfo_catalog_t target_mergeinfo_cat = NULL;
12334 source_peg_rev.kind = svn_opt_revision_number;
12335 source_peg_rev.value.number = source_branch->tip->rev;
12336 source_start_rev.kind = svn_opt_revision_number;
12337 source_start_rev.value.number = yca->rev;
12338 source_end_rev.kind = svn_opt_revision_number;
12339 source_end_rev.value.number = source_branch->tip->rev;
12340 target_opt_rev.kind = svn_opt_revision_number;
12341 target_opt_rev.value.number = target->rev;
12343 /* Find the youngest revision fully merged from SOURCE_BRANCH to TARGET,
12344 if such a revision exists. */
12345 SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
12346 TRUE, /* Find merged */
12347 target->url, &target_opt_rev,
12348 source_branch->tip->url,
12350 &source_end_rev, &source_start_rev,
12351 operative_rev_receiver,
12352 &youngest_merged_rev,
12354 result_pool, scratch_pool));
12356 if (!SVN_IS_VALID_REVNUM(youngest_merged_rev))
12358 /* No revisions have been completely merged from SOURCE_BRANCH to
12359 TARGET so the base for the next merge is the YCA. */
12364 /* One or more revisions have already been completely merged from
12365 SOURCE_BRANCH to TARGET, now find the oldest revision, older
12366 than the youngest merged revision, which is still eligible to
12367 be merged, if such exists. */
12368 branch_history_t *contiguous_source;
12369 svn_revnum_t base_rev;
12370 svn_revnum_t oldest_eligible_rev = SVN_INVALID_REVNUM;
12372 /* If the only revisions eligible are younger than the youngest merged
12373 revision we can simply assume that the youngest eligible revision
12374 is the youngest merged revision. Obviously this may not be true!
12375 The revisions between the youngest merged revision and the tip of
12376 the branch may have several inoperative revisions -- they may *all*
12377 be inoperative revisions! But for the purpose of this function
12378 (i.e. finding the youngest revision after the YCA where all revs have
12379 been merged) that doesn't matter. */
12380 source_end_rev.value.number = youngest_merged_rev;
12381 SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
12382 FALSE, /* Find eligible */
12383 target->url, &target_opt_rev,
12384 source_branch->tip->url,
12386 &source_start_rev, &source_end_rev,
12387 operative_rev_receiver,
12388 &oldest_eligible_rev,
12390 scratch_pool, scratch_pool));
12392 /* If there are revisions eligible for merging, use the oldest one
12393 to calculate the base. Otherwise there are no operative revisions
12394 to merge and we can simple set the base to the youngest revision
12396 if (SVN_IS_VALID_REVNUM(oldest_eligible_rev))
12397 base_rev = oldest_eligible_rev - 1;
12399 base_rev = youngest_merged_rev;
12401 /* Find the branch location just before the oldest eligible rev.
12402 (We can't just use the base revs calculated above because the branch
12403 might have a gap there.) */
12404 SVN_ERR(branch_history_intersect_range(&contiguous_source,
12405 source_branch, yca->rev,
12407 scratch_pool, scratch_pool));
12408 SVN_ERR(branch_history_get_endpoints(NULL, base_p, contiguous_source,
12409 result_pool, scratch_pool));
12412 return SVN_NO_ERROR;
12415 /* Find a merge base location on the target branch, like in a sync
12419 * o-------o-----------o---
12421 * -----o prev. \ \ this
12422 * YCA \ merge \ \ merge
12423 * o-----------o-----------o
12426 * Set *BASE_P to BASE, the youngest location in the history of S_T->source
12427 * (at or after the YCA) at which all revisions up to BASE are effectively
12428 * merged into S_T->target.
12430 * If no locations on the history of S_T->source are effectively merged to
12431 * S_T->target, set *BASE_P to the YCA.
12433 static svn_error_t *
12434 find_base_on_source(svn_client__pathrev_t **base_p,
12435 source_and_target_t *s_t,
12436 svn_client_ctx_t *ctx,
12437 apr_pool_t *result_pool,
12438 apr_pool_t *scratch_pool)
12440 SVN_ERR(find_last_merged_location(base_p,
12442 &s_t->source_branch,
12443 s_t->target_branch.tip,
12445 s_t->source_ra_session,
12446 result_pool, scratch_pool));
12447 return SVN_NO_ERROR;
12450 /* Find a merge base location on the target branch, like in a reintegrate
12454 * o-----------o-------o---
12456 * -----o merge / \ this
12458 * o-------o---------------o
12461 * Set *BASE_P to BASE, the youngest location in the history of S_T->target
12462 * (at or after the YCA) at which all revisions up to BASE are effectively
12463 * merged into S_T->source.
12465 * If no locations on the history of S_T->target are effectively merged to
12466 * S_T->source, set *BASE_P to the YCA.
12468 static svn_error_t *
12469 find_base_on_target(svn_client__pathrev_t **base_p,
12470 source_and_target_t *s_t,
12471 svn_client_ctx_t *ctx,
12472 apr_pool_t *result_pool,
12473 apr_pool_t *scratch_pool)
12475 SVN_ERR(find_last_merged_location(base_p,
12477 &s_t->target_branch,
12480 s_t->target_ra_session,
12481 result_pool, scratch_pool));
12483 return SVN_NO_ERROR;
12486 /* Find the last point at which the branch at S_T->source was completely
12487 * merged to the branch at S_T->target or vice-versa.
12489 * Fill in S_T->source_branch and S_T->target_branch and S_T->yca.
12490 * Set *BASE_P to the merge base. Set *IS_REINTEGRATE_LIKE to true if
12491 * an automatic merge from source to target would be a reintegration
12492 * merge: that is, if the last automatic merge was in the opposite
12493 * direction; or to false otherwise.
12495 * If there is no youngest common ancestor, throw an error.
12497 static svn_error_t *
12498 find_automatic_merge(svn_client__pathrev_t **base_p,
12499 svn_boolean_t *is_reintegrate_like,
12500 source_and_target_t *s_t,
12501 svn_client_ctx_t *ctx,
12502 apr_pool_t *result_pool,
12503 apr_pool_t *scratch_pool)
12505 svn_client__pathrev_t *base_on_source, *base_on_target;
12507 /* Get the location-history of each branch. */
12508 s_t->source_branch.tip = s_t->source;
12509 SVN_ERR(svn_client__get_history_as_mergeinfo(
12510 &s_t->source_branch.history, &s_t->source_branch.has_r0_history,
12511 s_t->source, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
12512 s_t->source_ra_session, ctx, scratch_pool));
12513 s_t->target_branch.tip = &s_t->target->loc;
12514 SVN_ERR(svn_client__get_history_as_mergeinfo(
12515 &s_t->target_branch.history, &s_t->target_branch.has_r0_history,
12516 &s_t->target->loc, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
12517 s_t->target_ra_session, ctx, scratch_pool));
12519 SVN_ERR(svn_client__calc_youngest_common_ancestor(
12520 &s_t->yca, s_t->source, s_t->source_branch.history,
12521 s_t->source_branch.has_r0_history,
12522 &s_t->target->loc, s_t->target_branch.history,
12523 s_t->target_branch.has_r0_history,
12524 result_pool, scratch_pool));
12527 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
12528 _("'%s@%ld' must be ancestrally related to "
12530 s_t->source->url, s_t->source->rev,
12531 s_t->target->loc.url, s_t->target->loc.rev);
12533 /* Find the latest revision of A synced to B and the latest
12534 * revision of B synced to A.
12536 * base_on_source = youngest_complete_synced_point(source, target)
12537 * base_on_target = youngest_complete_synced_point(target, source)
12539 SVN_ERR(find_base_on_source(&base_on_source, s_t,
12540 ctx, scratch_pool, scratch_pool));
12541 SVN_ERR(find_base_on_target(&base_on_target, s_t,
12542 ctx, scratch_pool, scratch_pool));
12544 /* Choose a base. */
12545 if (base_on_source->rev >= base_on_target->rev)
12547 *base_p = base_on_source;
12548 *is_reintegrate_like = FALSE;
12552 *base_p = base_on_target;
12553 *is_reintegrate_like = TRUE;
12556 return SVN_NO_ERROR;
12559 /** Find out what kind of automatic merge would be needed, when the target
12560 * is only known as a repository location rather than a WC.
12562 * Like find_automatic_merge() except that the target is
12563 * specified by @a target_path_or_url at @a target_revision, which must
12564 * refer to a repository location, instead of by a WC path argument.
12566 * Set *MERGE_P to a new structure with all fields filled in except the
12569 static svn_error_t *
12570 find_automatic_merge_no_wc(automatic_merge_t **merge_p,
12571 const char *source_path_or_url,
12572 const svn_opt_revision_t *source_revision,
12573 const char *target_path_or_url,
12574 const svn_opt_revision_t *target_revision,
12575 svn_client_ctx_t *ctx,
12576 apr_pool_t *result_pool,
12577 apr_pool_t *scratch_pool)
12579 source_and_target_t *s_t = apr_palloc(scratch_pool, sizeof(*s_t));
12580 svn_client__pathrev_t *target_loc;
12581 automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
12584 SVN_ERR(svn_client__ra_session_from_path2(
12585 &s_t->source_ra_session, &s_t->source,
12586 source_path_or_url, NULL, source_revision, source_revision,
12587 ctx, result_pool));
12590 SVN_ERR(svn_client__ra_session_from_path2(
12591 &s_t->target_ra_session, &target_loc,
12592 target_path_or_url, NULL, target_revision, target_revision,
12593 ctx, result_pool));
12594 s_t->target = apr_palloc(scratch_pool, sizeof(*s_t->target));
12595 s_t->target->abspath = NULL; /* indicate the target is not a WC */
12596 s_t->target->loc = *target_loc;
12598 SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
12599 ctx, result_pool, scratch_pool));
12601 merge->right = s_t->source;
12602 merge->target = &s_t->target->loc;
12603 merge->yca = s_t->yca;
12606 return SVN_NO_ERROR;
12609 /* Find the information needed to merge all unmerged changes from a source
12610 * branch into a target branch.
12612 * Set @a *merge_p to the information needed to merge all unmerged changes
12613 * (up to @a source_revision) from the source branch @a source_path_or_url
12614 * at @a source_revision into the target WC at @a target_abspath.
12616 * The flags @a allow_mixed_rev, @a allow_local_mods and
12617 * @a allow_switched_subtrees enable merging into a WC that is in any or all
12618 * of the states described by their names, but only if this function decides
12619 * that the merge will be in the same direction as the last automatic merge.
12620 * If, on the other hand, the last automatic merge was in the opposite
12621 * direction, then such states of the WC are not allowed regardless
12622 * of these flags. This function merely records these flags in the
12623 * @a *merge_p structure; do_automatic_merge_locked() checks the WC
12624 * state for compliance.
12626 * Allocate the @a *merge_p structure in @a result_pool.
12628 static svn_error_t *
12629 client_find_automatic_merge(automatic_merge_t **merge_p,
12630 const char *source_path_or_url,
12631 const svn_opt_revision_t *source_revision,
12632 const char *target_abspath,
12633 svn_boolean_t allow_mixed_rev,
12634 svn_boolean_t allow_local_mods,
12635 svn_boolean_t allow_switched_subtrees,
12636 svn_client_ctx_t *ctx,
12637 apr_pool_t *result_pool,
12638 apr_pool_t *scratch_pool)
12640 source_and_target_t *s_t = apr_palloc(result_pool, sizeof(*s_t));
12641 automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
12643 SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
12645 /* "Open" the target WC. Check the target WC for mixed-rev, local mods and
12646 * switched subtrees yet to faster exit and notify user before contacting
12647 * with server. After we find out what kind of merge is required, then if a
12648 * reintegrate-like merge is required we'll do the stricter checks, in
12649 * do_automatic_merge_locked(). */
12650 SVN_ERR(open_target_wc(&s_t->target, target_abspath,
12653 allow_switched_subtrees,
12654 ctx, result_pool, scratch_pool));
12656 if (!s_t->target->loc.url)
12657 return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
12658 _("Can't perform automatic merge into '%s' "
12659 "because it is locally added and therefore "
12660 "not related to the merge source"),
12661 svn_dirent_local_style(target_abspath,
12664 /* Open RA sessions to the source and target trees. */
12665 SVN_ERR(svn_client_open_ra_session2(&s_t->target_ra_session,
12666 s_t->target->loc.url,
12667 s_t->target->abspath,
12668 ctx, result_pool, scratch_pool));
12669 SVN_ERR(svn_client__ra_session_from_path2(
12670 &s_t->source_ra_session, &s_t->source,
12671 source_path_or_url, NULL, source_revision, source_revision,
12672 ctx, result_pool));
12674 /* Check source is in same repos as target. */
12675 SVN_ERR(check_same_repos(s_t->source, source_path_or_url,
12676 &s_t->target->loc, target_abspath,
12677 TRUE /* strict_urls */, scratch_pool));
12679 SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
12680 ctx, result_pool, scratch_pool));
12681 merge->yca = s_t->yca;
12682 merge->right = s_t->source;
12683 merge->target = &s_t->target->loc;
12684 merge->allow_mixed_rev = allow_mixed_rev;
12685 merge->allow_local_mods = allow_local_mods;
12686 merge->allow_switched_subtrees = allow_switched_subtrees;
12690 /* TODO: Close the source and target sessions here? */
12692 return SVN_NO_ERROR;
12695 /* Perform an automatic merge, given the information in MERGE which
12696 * must have come from calling client_find_automatic_merge().
12698 * Four locations are inputs: YCA, BASE, RIGHT, TARGET, as shown
12699 * depending on whether the base is on the source branch or the target
12700 * branch of this merge.
12702 * RIGHT (is_reintegrate_like)
12703 * o-----------o-------o---
12705 * -----o merge / \ this
12707 * o-------o---------------o
12712 * BASE RIGHT (! is_reintegrate_like)
12713 * o-------o-----------o---
12715 * -----o prev. \ \ this
12716 * YCA \ merge \ \ merge
12717 * o-----------o-----------o
12720 * ### TODO: The reintegrate-like code path does not yet
12721 * eliminate already-cherry-picked revisions from the source.
12723 static svn_error_t *
12724 do_automatic_merge_locked(conflict_report_t **conflict_report,
12725 const automatic_merge_t *merge,
12726 const char *target_abspath,
12728 svn_boolean_t diff_ignore_ancestry,
12729 svn_boolean_t force_delete,
12730 svn_boolean_t record_only,
12731 svn_boolean_t dry_run,
12732 const apr_array_header_t *merge_options,
12733 svn_client_ctx_t *ctx,
12734 apr_pool_t *result_pool,
12735 apr_pool_t *scratch_pool)
12737 merge_target_t *target;
12738 svn_boolean_t reintegrate_like = merge->is_reintegrate_like;
12739 svn_boolean_t use_sleep = FALSE;
12742 SVN_ERR(open_target_wc(&target, target_abspath,
12743 merge->allow_mixed_rev && ! reintegrate_like,
12744 merge->allow_local_mods && ! reintegrate_like,
12745 merge->allow_switched_subtrees && ! reintegrate_like,
12746 ctx, scratch_pool, scratch_pool));
12748 if (reintegrate_like)
12750 merge_source_t source;
12751 svn_ra_session_t *base_ra_session = NULL;
12752 svn_ra_session_t *right_ra_session = NULL;
12753 svn_ra_session_t *target_ra_session = NULL;
12756 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12757 _("The required merge is reintegrate-like, "
12758 "and the record-only option "
12759 "cannot be used with this kind of merge"));
12761 if (depth != svn_depth_unknown)
12762 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12763 _("The required merge is reintegrate-like, "
12764 "and the depth option "
12765 "cannot be used with this kind of merge"));
12768 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12769 _("The required merge is reintegrate-like, "
12770 "and the force_delete option "
12771 "cannot be used with this kind of merge"));
12773 SVN_ERR(ensure_ra_session_url(&base_ra_session, merge->base->url,
12774 target->abspath, ctx, scratch_pool));
12775 SVN_ERR(ensure_ra_session_url(&right_ra_session, merge->right->url,
12776 target->abspath, ctx, scratch_pool));
12777 SVN_ERR(ensure_ra_session_url(&target_ra_session, target->loc.url,
12778 target->abspath, ctx, scratch_pool));
12780 /* Check for and reject any abnormalities -- such as revisions that
12781 * have not yet been merged in the opposite direction -- that a
12782 * 'reintegrate' merge would have rejected. */
12784 merge_source_t *source2;
12786 SVN_ERR(find_reintegrate_merge(&source2, NULL,
12787 right_ra_session, merge->right,
12788 target_ra_session, target,
12789 ctx, scratch_pool, scratch_pool));
12792 source.loc1 = merge->base;
12793 source.loc2 = merge->right;
12794 source.ancestral = ! merge->is_reintegrate_like;
12796 err = merge_cousins_and_supplement_mergeinfo(conflict_report,
12801 &source, merge->yca,
12802 TRUE /* same_repos */,
12804 FALSE /*diff_ignore_ancestry*/,
12805 force_delete, record_only,
12809 result_pool, scratch_pool);
12811 else /* ! merge->is_reintegrate_like */
12813 /* Ignoring the base that we found, we pass the YCA instead and let
12814 do_merge() work out which subtrees need which revision ranges to
12815 be merged. This enables do_merge() to fill in revision-range
12816 gaps that are older than the base that we calculated (which is
12817 for the root path of the merge).
12819 An improvement would be to change find_automatic_merge() to
12820 find the base for each sutree, and then here use the oldest base
12821 among all subtrees. */
12822 apr_array_header_t *merge_sources;
12823 svn_ra_session_t *ra_session = NULL;
12825 /* Normalize our merge sources, do_merge() requires this. See the
12826 'MERGEINFO MERGE SOURCE NORMALIZATION' global comment. */
12827 SVN_ERR(ensure_ra_session_url(&ra_session, merge->right->url,
12828 target->abspath, ctx, scratch_pool));
12829 SVN_ERR(normalize_merge_sources_internal(
12830 &merge_sources, merge->right,
12831 svn_rangelist__initialize(merge->yca->rev, merge->right->rev, TRUE,
12833 ra_session, ctx, scratch_pool, scratch_pool));
12835 err = do_merge(NULL, NULL, conflict_report, &use_sleep,
12836 merge_sources, target, ra_session,
12837 TRUE /*related*/, TRUE /*same_repos*/,
12838 FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
12839 force_delete, dry_run,
12840 record_only, NULL, FALSE, FALSE, depth, merge_options,
12841 ctx, result_pool, scratch_pool);
12845 svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
12849 return SVN_NO_ERROR;
12853 svn_client_get_merging_summary(svn_boolean_t *needs_reintegration,
12854 const char **yca_url, svn_revnum_t *yca_rev,
12855 const char **base_url, svn_revnum_t *base_rev,
12856 const char **right_url, svn_revnum_t *right_rev,
12857 const char **target_url, svn_revnum_t *target_rev,
12858 const char **repos_root_url,
12859 const char *source_path_or_url,
12860 const svn_opt_revision_t *source_revision,
12861 const char *target_path_or_url,
12862 const svn_opt_revision_t *target_revision,
12863 svn_client_ctx_t *ctx,
12864 apr_pool_t *result_pool,
12865 apr_pool_t *scratch_pool)
12867 svn_boolean_t target_is_wc;
12868 automatic_merge_t *merge;
12870 target_is_wc = (! svn_path_is_url(target_path_or_url))
12871 && (target_revision->kind == svn_opt_revision_unspecified
12872 || target_revision->kind == svn_opt_revision_working
12873 || target_revision->kind == svn_opt_revision_base);
12876 const char *target_abspath;
12878 SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_path_or_url,
12880 SVN_ERR(client_find_automatic_merge(
12882 source_path_or_url, source_revision,
12884 TRUE, TRUE, TRUE, /* allow_* */
12885 ctx, scratch_pool, scratch_pool));
12888 SVN_ERR(find_automatic_merge_no_wc(
12890 source_path_or_url, source_revision,
12891 target_path_or_url, target_revision,
12892 ctx, scratch_pool, scratch_pool));
12894 if (needs_reintegration)
12895 *needs_reintegration = merge->is_reintegrate_like;
12897 *yca_url = apr_pstrdup(result_pool, merge->yca->url);
12899 *yca_rev = merge->yca->rev;
12901 *base_url = apr_pstrdup(result_pool, merge->base->url);
12903 *base_rev = merge->base->rev;
12905 *right_url = apr_pstrdup(result_pool, merge->right->url);
12907 *right_rev = merge->right->rev;
12909 *target_url = apr_pstrdup(result_pool, merge->target->url);
12911 *target_rev = merge->target->rev;
12912 if (repos_root_url)
12913 *repos_root_url = apr_pstrdup(result_pool, merge->yca->repos_root_url);
12915 return SVN_NO_ERROR;