Change the format of the revprops block sent in svnserve for
[svn.git] / subversion / libsvn_client / merge.c
blobf76e45551957df719ab54cc63f6366a54e64abab
1 /*
2 * merge.c: merging
4 * ====================================================================
5 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 /* ==================================================================== */
23 /*** Includes ***/
25 #include <apr_strings.h>
26 #include <apr_tables.h>
27 #include <apr_hash.h>
28 #include "svn_types.h"
29 #include "svn_hash.h"
30 #include "svn_wc.h"
31 #include "svn_delta.h"
32 #include "svn_diff.h"
33 #include "svn_mergeinfo.h"
34 #include "svn_client.h"
35 #include "svn_string.h"
36 #include "svn_error.h"
37 #include "svn_path.h"
38 #include "svn_io.h"
39 #include "svn_utf.h"
40 #include "svn_pools.h"
41 #include "svn_config.h"
42 #include "svn_props.h"
43 #include "svn_time.h"
44 #include "svn_sorts.h"
45 #include "svn_ra.h"
46 #include "client.h"
47 #include "mergeinfo.h"
48 #include <assert.h>
50 #include "private/svn_wc_private.h"
51 #include "private/svn_mergeinfo_private.h"
53 #include "svn_private_config.h"
55 /*-----------------------------------------------------------------------*/
57 /* MERGEINFO MERGE SOURCE NORMALIZATION
59 * Nearly any helper function herein that accepts two URL/revision
60 * pairs expects one of two things to be true:
62 * 1. that mergeinfo is not being recorded at all for this
63 * operation, or
65 * 2. that the pairs represent two locations along a single line
66 * of version history such that there are no copies in the
67 * history of the object between the locations when treating
68 * the oldest of the two locations as non-inclusive. In other
69 * words, if there is a copy at all between them, there is only
70 * one copy and its source was the oldest of the two locations.
72 * We use svn_ra_get_location_segments() to split a given range of
73 * revisions across an object's history into several which obey these
74 * rules. For example, a merge between r19500 and r27567 of
75 * Subversion's own /tags/1.4.5 directory gets split into sequential
76 * merges of the following location pairs:
78 * [/trunk:19549, /trunk:19523]
79 * (recorded in svn:mergeinfo as /trunk:19500-19523)
81 * [/trunk:19523, /branches/1.4.x:25188]
82 * (recorded in svn:mergeinfo as /branches/1.4.x:19524-25188)
84 * [/branches/1.4.x:25188, /tags/1.4.4@26345]
85 * (recorded in svn:mergeinfo as /tags/1.4.4:25189-26345)
87 * [/tags/1.4.4@26345, /branches/1.4.5@26350]
88 * (recorded in svn:mergeinfo as /branches/1.4.5:26346-26350)
90 * [/branches/1.4.5@26350, /tags/1.4.5@27567]
91 * (recorded in svn:mergeinfo as /tags/1.4.5:26351-27567)
93 * Our helper functions would then operate on one of these location
94 * pairs at a time.
97 /* WHICH SVN_CLIENT_MERGE* API DO I WANT?
99 * libsvn_client has three public merge APIs; they are all wrappers
100 * around the do_merge engine. Which one to use depends on the number
101 * of URLs passed as arguments and whether or not specific merge
102 * ranges (-c/-r) are specified.
104 * 1 URL 2 URLs
105 * +--------------------------------+---------------------+
106 * -c | mergeinfo-driven | |
107 * or | cherrypicking | unsupported |
108 * -r | (svn_client_merge_peg) | |
109 * +--------------------------------+---------------------+
110 * no | mergeinfo-driven | mergeinfo-oblivious|
111 * -c | whole-branch | diff-and-apply |
112 * or | heuristic merge | (svn_client_merge) |
113 * -r | (svn_client_merge_reintegrate) | |
114 * +--------------------------------+---------------------+
120 /*-----------------------------------------------------------------------*/
122 /*** Utilities ***/
124 /* Sanity check -- ensure that we have valid revisions to look at. */
125 #define ENSURE_VALID_REVISION_KINDS(rev1_kind, rev2_kind) \
128 /* Return SVN_ERR_UNSUPPORTED_FEATURE if URL's scheme does not
129 match the scheme of the url for ADM_ACCESS's path; return
130 SVN_ERR_BAD_URL if no scheme can be found for one or both urls;
131 otherwise return SVN_NO_ERROR. Use ADM_ACCESS's pool for
132 temporary allocation. */
133 static svn_error_t *
134 check_scheme_match(svn_wc_adm_access_t *adm_access, const char *url)
136 const char *path = svn_wc_adm_access_path(adm_access);
137 apr_pool_t *pool = svn_wc_adm_access_pool(adm_access);
138 const svn_wc_entry_t *ent;
139 const char *idx1, *idx2;
141 SVN_ERR(svn_wc_entry(&ent, path, adm_access, TRUE, pool));
143 idx1 = strchr(url, ':');
144 idx2 = strchr(ent->url, ':');
146 if ((idx1 == NULL) && (idx2 == NULL))
148 return svn_error_createf
149 (SVN_ERR_BAD_URL, NULL,
150 _("URLs have no scheme ('%s' and '%s')"), url, ent->url);
152 else if (idx1 == NULL)
154 return svn_error_createf
155 (SVN_ERR_BAD_URL, NULL,
156 _("URL has no scheme: '%s'"), url);
158 else if (idx2 == NULL)
160 return svn_error_createf
161 (SVN_ERR_BAD_URL, NULL,
162 _("URL has no scheme: '%s'"), ent->url);
164 else if (((idx1 - url) != (idx2 - ent->url))
165 || (strncmp(url, ent->url, idx1 - url) != 0))
167 return svn_error_createf
168 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
169 _("Access scheme mixtures not yet supported ('%s' and '%s')"),
170 url, ent->url);
173 /* else */
175 return SVN_NO_ERROR;
179 /*-----------------------------------------------------------------------*/
181 /*** Repos-Diff Editor Callbacks ***/
184 /* Wrapper around svn_string_t, see merge_cmd_baton_t's
185 working_mergeinfo member. */
186 typedef struct {
187 /* Working mergeinfo for a path prior to a merge. May be NULL
188 if the path has no working mergeinfo. */
189 svn_string_t *working_mergeinfo_propval;
190 } working_mergeinfo_t;
192 typedef struct merge_cmd_baton_t {
193 svn_boolean_t force;
194 svn_boolean_t dry_run;
195 svn_boolean_t record_only; /* Whether to only record mergeinfo. */
196 svn_boolean_t sources_ancestral; /* Whether the left-side merge source is
197 an ancestor of the right-side, or
198 vice-versa (history-wise). */
199 svn_boolean_t same_repos; /* Whether the merge source repository
200 is the same repository as the
201 target. Defaults to FALSE if DRY_RUN
202 is TRUE.*/
203 svn_boolean_t mergeinfo_capable; /* Whether the merge source server
204 is capable of Merge Tracking. */
205 svn_boolean_t ignore_ancestry; /* Are we ignoring ancestry (and by
206 extension, mergeinfo)? FALSE if
207 SOURCES_ANCESTRAL is FALSE. */
208 svn_boolean_t target_missing_child; /* Whether working copy target of the
209 merge is missing any immediate
210 children. */
211 svn_boolean_t operative_merge; /* Whether any changes were actually
212 made as a result of this merge. */
213 svn_boolean_t first_range; /* If this is the first of possibly many
214 discontinuous ranges being merged. */
215 const char *added_path; /* Set to the dir path whenever the
216 dir is added as a child of a
217 versioned dir (dry-run only) */
218 const char *target; /* Working copy target of merge */
219 const char *url; /* The second URL in the merge */
220 svn_client_ctx_t *ctx; /* Client context for callbacks, etc. */
222 /* Whether invocation of the merge_file_added() callback required
223 delegation to the merge_file_changed() function for the file
224 currently being merged. This info is used to detect whether a
225 file on the left side of a 3-way merge actually exists (important
226 because it's created as an empty temp file on disk regardless).*/
227 svn_boolean_t add_necessitated_merge;
229 /* The list of paths for entries we've deleted, used only when in
230 dry_run mode. */
231 apr_hash_t *dry_run_deletions;
233 /* The list of any paths which remained in conflict after a
234 resolution attempt was made. We track this in-memory, rather
235 than just using WC entry state, since the latter doesn't help us
236 when in dry_run mode. */
237 apr_hash_t *conflicted_paths;
239 /* A merge requiring multiple editor drives will alter the TARGET tree's
240 mergeinfo on each editor drive. But the entire merge might eventually
241 be a no-op, in which case we need to return the tree's mergeinfo to its
242 working state prior to the start of the merge. This hash of const
243 char * paths mapped to const WORKING_MERGEINFO_T * records that pre-merge
244 working mergeinfo. Why not just map to a svn_string_t *? Because we
245 need to be able record if a path had *no* explicit working mergeinfo
246 prior to the merge. */
247 apr_hash_t *working_mergeinfo;
249 /* The diff3_cmd in ctx->config, if any, else null. We could just
250 extract this as needed, but since more than one caller uses it,
251 we just set it up when this baton is created. */
252 const char *diff3_cmd;
253 const apr_array_header_t *merge_options;
255 /* RA sessions used throughout a merge operation. Opened/re-parented
256 as needed.
258 NOTE: During the actual merge editor drive, RA_SESSION1 is used
259 for the primary editing and RA_SESSION2 for fetching additional
260 information -- as necessary -- from the repository. So during
261 this phase of the merge, you *must not* reparent RA_SESSION1; use
262 (temporarily reparenting if you must) RA_SESSION2 instead. */
263 svn_ra_session_t *ra_session1;
264 svn_ra_session_t *ra_session2;
266 /* Flag indicating the fact target has everything merged already,
267 for the sake of children's merge to work it sets itself a dummy
268 merge range of requested_end_rev:requested_end_rev. */
269 svn_boolean_t target_has_dummy_merge_range;
271 /* Pool which has a lifetime limited to one iteration over a given
272 merge source, i.e. it is cleared on every call to do_directory_merge()
273 or do_file_merge() in do_merge(). */
274 apr_pool_t *pool;
275 /* Pool which has a lifetime at least as long as do_merge()'s
276 iteration over all merge sources. */
277 apr_pool_t *long_pool;
278 } merge_cmd_baton_t;
280 apr_hash_t *
281 svn_client__dry_run_deletions(void *merge_cmd_baton)
283 merge_cmd_baton_t *merge_b = merge_cmd_baton;
284 return merge_b->dry_run_deletions;
287 /* Used to avoid spurious notifications (e.g. conflicts) from a merge
288 attempt into an existing target which would have been deleted if we
289 weren't in dry_run mode (issue #2584). Assumes that WCPATH is
290 still versioned (e.g. has an associated entry). */
291 static APR_INLINE svn_boolean_t
292 dry_run_deleted_p(merge_cmd_baton_t *merge_b, const char *wcpath)
294 return (merge_b->dry_run &&
295 apr_hash_get(merge_b->dry_run_deletions, wcpath,
296 APR_HASH_KEY_STRING) != NULL);
299 /* Return whether any WC path was put in conflict by the merge
300 operation corresponding to MERGE_B. */
301 static APR_INLINE svn_boolean_t
302 is_path_conflicted_by_merge(merge_cmd_baton_t *merge_b)
304 return (merge_b->conflicted_paths &&
305 apr_hash_count(merge_b->conflicted_paths) > 0);
308 /* Set *HONOR_MERGEINFO and *RECORD_MERGEINFO (if non-NULL)
309 appropriately for MERGE_B. */
310 static APR_INLINE void
311 mergeinfo_behavior(svn_boolean_t *honor_mergeinfo,
312 svn_boolean_t *record_mergeinfo,
313 merge_cmd_baton_t *merge_b)
315 if (honor_mergeinfo)
316 *honor_mergeinfo = (merge_b->mergeinfo_capable
317 && merge_b->sources_ancestral
318 && merge_b->same_repos
319 && (! merge_b->ignore_ancestry));
321 if (record_mergeinfo)
322 *record_mergeinfo = (merge_b->mergeinfo_capable
323 && merge_b->sources_ancestral
324 && merge_b->same_repos
325 && (! merge_b->dry_run));
328 /* Helper for merge_props_changed(). Filter out mergeinfo property additions
329 to PATH when those additions refer to the same line of history.
331 *PROPS is an array of svn_prop_t structures representing regular properties
332 to be added to the working copy PATH. ADM_ACCESS and MERGE_B are cascaded
333 from merge_props_changed().
335 If mergeinfo is not being honored, do nothing. Otherwise examine the added
336 mergeinfo, looking at each range (or single rev) of each source path. If a
337 source_path/range refers to the same line of history as PATH (pegged at its
338 base revision), then filter out that range. If the entire rangelist for a
339 given path is filtered then filter out the path as well. Set outgoing
340 *PROPS to a shallow copy (allocated in POOL) of incoming *PROPS minus the
341 filtered self-referential mergeinfo. */
342 static svn_error_t*
343 filter_self_referential_mergeinfo(apr_array_header_t **props,
344 const char *path,
345 merge_cmd_baton_t *merge_b,
346 svn_wc_adm_access_t *adm_access,
347 apr_pool_t *pool)
349 svn_boolean_t honor_mergeinfo;
350 apr_array_header_t *adjusted_props;
351 int i;
353 /* If we aren't honoring mergeinfo, get outta here. */
354 mergeinfo_behavior(&honor_mergeinfo, NULL, merge_b);
355 if (! honor_mergeinfo)
356 return SVN_NO_ERROR;
358 adjusted_props = apr_array_make(pool, (*props)->nelts, sizeof(svn_prop_t));
359 for (i = 0; i < (*props)->nelts; ++i)
361 svn_prop_t *prop = &APR_ARRAY_IDX((*props), i, svn_prop_t);
363 /* If this property isn't mergeinfo or is empty mergeinfo it
364 does not require any special handling. */
365 if ((strcmp(prop->name, SVN_PROP_MERGEINFO) != 0)
366 || (! prop->value))
368 APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
370 else /* Non-empty mergeinfo; filter self-referential mergeinfo out. */
372 svn_mergeinfo_t mergeinfo;
373 apr_hash_index_t *hi;
374 const char *target_url, *merge_source_root_url;
375 const svn_wc_entry_t *target_entry;
376 const char *old_url = NULL;
378 SVN_ERR(svn_ra_get_repos_root2(merge_b->ra_session2,
379 &merge_source_root_url, pool));
381 /* Get an entry for PATH so we can find its base revision. */
382 SVN_ERR(svn_wc__entry_versioned(&target_entry, path, adm_access,
383 FALSE, pool));
385 /* Temporarily reparent our RA session to the merge
386 target's URL. */
387 SVN_ERR(svn_client_url_from_path(&target_url, path, pool));
388 SVN_ERR(svn_client__ensure_ra_session_url(&old_url,
389 merge_b->ra_session2,
390 target_url, pool));
392 /* Parse the incoming mergeinfo to allow easier meddling. */
393 SVN_ERR(svn_mergeinfo_parse(&mergeinfo, prop->value->data, pool));
395 for (hi = apr_hash_first(NULL, mergeinfo);
396 hi; hi = apr_hash_next(hi))
398 int j;
399 const void *key;
400 void *value;
401 const char *source_path;
402 apr_array_header_t *rangelist;
403 apr_array_header_t *adjusted_rangelist =
404 apr_array_make(pool, 0, sizeof(svn_merge_range_t *));
406 apr_hash_this(hi, &key, NULL, &value);
407 source_path = key;
408 rangelist = value;
410 for (j = 0; j < rangelist->nelts; j++)
412 svn_error_t *err;
413 svn_opt_revision_t *start_revision, *end_revision;
414 const char *start_url, *end_url;
415 svn_opt_revision_t peg_rev, rev1_opt, rev2_opt;
416 svn_merge_range_t *range =
417 APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *);
418 const char *merge_source_url =
419 svn_path_join(merge_source_root_url,
420 source_path + 1, pool);
422 peg_rev.kind = svn_opt_revision_number;
423 peg_rev.value.number = target_entry->revision;
424 rev1_opt.kind = svn_opt_revision_number;
425 rev1_opt.value.number = range->start;
427 /* Because the merge source normalization code
428 ensures mergeinfo refers to real locations on
429 the same line of history, there's no need to
430 look at the whole range, just the start. */
431 rev2_opt.kind = svn_opt_revision_unspecified;
433 /* Check if PATH@TARGET_ENTRY->REVISION exists at
434 RANGE->START on the same line of history. */
435 err = svn_client__repos_locations(&start_url,
436 &start_revision,
437 &end_url,
438 &end_revision,
439 merge_b->ra_session2,
440 target_url,
441 &peg_rev,
442 &rev1_opt,
443 &rev2_opt,
444 merge_b->ctx,
445 pool);
446 if (err)
448 if (err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES
449 || err->apr_err == SVN_ERR_RA_DAV_PATH_NOT_FOUND
450 || err->apr_err == SVN_ERR_FS_NOT_FOUND)
452 /* PATH@TARGET_ENTRY->REVISION didn't exist at
453 RANGE->START or is unrelated to the resource
454 PATH@RANGE->START. Either way we don't
455 filter. */
456 svn_error_clear(err);
457 err = NULL;
458 APR_ARRAY_PUSH(adjusted_rangelist,
459 svn_merge_range_t *) = range;
461 else
463 return err;
466 else
468 /* PATH@TARGET_ENTRY->REVISION exists on the same
469 line of history at RANGE->START. But it might
470 have existed under a different name then, so
471 check if the URL it had then is the same as the
472 URL for the mergeinfo we are trying to add. If
473 it is the same we can filter it out. */
474 if (strcmp(start_url, merge_source_url) != 0)
476 APR_ARRAY_PUSH(adjusted_rangelist,
477 svn_merge_range_t *) = range;
480 } /* for (j = 0; j < rangelist->nelts; j++) */
482 /* If only some of the ranges mapped from SOURCE_PATH were
483 filtered then create a new svn_prop_t to represent
484 this. */
485 if (adjusted_rangelist->nelts)
487 svn_string_t *adjusted_rangelist_s;
488 svn_prop_t *adjusted_prop =
489 apr_pcalloc(pool, sizeof(*adjusted_prop));
491 SVN_ERR(svn_rangelist_to_string(&adjusted_rangelist_s,
492 adjusted_rangelist,
493 pool));
494 adjusted_prop->name = SVN_PROP_MERGEINFO;
495 adjusted_prop->value =
496 svn_string_create(apr_pstrcat(pool, source_path, ":",
497 adjusted_rangelist_s->data,
498 NULL),
499 pool);
500 APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *adjusted_prop;
502 } /* mergeinfo hash iteration */
504 /* If we reparented MERGE_B->RA_SESSION2 above, put it back
505 to the original URL. */
506 if (old_url)
507 SVN_ERR(svn_ra_reparent(merge_b->ra_session2, old_url, pool));
509 } /* Property is non-empty mergeinfo. */
510 } /* (i = 0; i < (*props)->nelts; ++i) */
512 *props = adjusted_props;
513 return SVN_NO_ERROR;
517 /* A svn_wc_diff_callbacks2_t function. Used for both file and directory
518 property merges. */
519 static svn_error_t *
520 merge_props_changed(svn_wc_adm_access_t *adm_access,
521 svn_wc_notify_state_t *state,
522 const char *path,
523 const apr_array_header_t *propchanges,
524 apr_hash_t *original_props,
525 void *baton)
527 apr_array_header_t *props;
528 merge_cmd_baton_t *merge_b = baton;
529 svn_client_ctx_t *ctx = merge_b->ctx;
530 apr_pool_t *subpool = svn_pool_create(merge_b->pool);
531 svn_error_t *err;
533 SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props, subpool));
535 /* We only want to merge "regular" version properties: by
536 definition, 'svn merge' shouldn't touch any data within .svn/ */
537 if (props->nelts)
539 /* svn_wc_merge_props() requires ADM_ACCESS to be the access for
540 the parent of PATH. Since the advent of merge tracking,
541 do_directory_merge() may call this (indirectly) with
542 the access for the merge_b->target instead (issue #2781).
543 So, if we have the wrong access, get the right one. */
544 if (svn_path_compare_paths(svn_wc_adm_access_path(adm_access),
545 path) != 0)
546 SVN_ERR(svn_wc_adm_probe_try3(&adm_access, adm_access, path,
547 TRUE, -1, ctx->cancel_func,
548 ctx->cancel_baton, subpool));
550 /* Don't add mergeinfo from PATH's own history. */
551 SVN_ERR(filter_self_referential_mergeinfo(&props, path, merge_b,
552 adm_access, subpool));
554 err = svn_wc_merge_props2(state, path, adm_access, original_props, props,
555 FALSE, merge_b->dry_run, ctx->conflict_func,
556 ctx->conflict_baton, subpool);
557 if (err && (err->apr_err == SVN_ERR_ENTRY_NOT_FOUND
558 || err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE))
560 /* if the entry doesn't exist in the wc, just 'skip' over
561 this part of the tree-delta. */
562 if (state)
563 *state = svn_wc_notify_state_missing;
564 svn_error_clear(err);
565 svn_pool_destroy(subpool);
566 return SVN_NO_ERROR;
568 else if (err)
569 return err;
572 svn_pool_destroy(subpool);
573 return SVN_NO_ERROR;
576 /* Contains any state collected while resolving conflicts. */
577 typedef struct
579 /* The wrapped callback and baton. */
580 svn_wc_conflict_resolver_func_t wrapped_func;
581 void *wrapped_baton;
583 /* The list of any paths which remained in conflict after a
584 resolution attempt was made. */
585 apr_hash_t **conflicted_paths;
587 /* Pool used in notification_receiver() to avoid the iteration
588 sub-pool which is passed in, then subsequently destroyed. */
589 apr_pool_t *pool;
590 } conflict_resolver_baton_t;
592 /* An implementation of the svn_wc_conflict_resolver_func_t interface.
593 We keep a record of paths which remain in conflict after any
594 resolution attempt from BATON->wrapped_func. */
595 static svn_error_t *
596 conflict_resolver(svn_wc_conflict_result_t **result,
597 const svn_wc_conflict_description_t *description,
598 void *baton, apr_pool_t *pool)
600 svn_error_t *err;
601 conflict_resolver_baton_t *conflict_b = baton;
603 if (conflict_b->wrapped_func)
604 err = (*conflict_b->wrapped_func)(result, description,
605 conflict_b->wrapped_baton, pool);
606 else
608 /* If we have no wrapped callback to invoke, then we still need
609 to behave like a proper conflict-callback ourselves. */
610 *result = svn_wc_create_conflict_result(svn_wc_conflict_choose_postpone,
611 NULL, pool);
612 err = SVN_NO_ERROR;
615 /* Keep a record of paths still in conflict after the resolution attempt. */
616 if ((! conflict_b->wrapped_func)
617 || (*result && ((*result)->choice == svn_wc_conflict_choose_postpone)))
619 const char *conflicted_path = apr_pstrdup(conflict_b->pool,
620 description->path);
622 if (*conflict_b->conflicted_paths == NULL)
623 *conflict_b->conflicted_paths = apr_hash_make(conflict_b->pool);
625 apr_hash_set(*conflict_b->conflicted_paths, conflicted_path,
626 APR_HASH_KEY_STRING, conflicted_path);
629 return err;
632 /* A svn_wc_diff_callbacks2_t function. */
633 static svn_error_t *
634 merge_file_changed(svn_wc_adm_access_t *adm_access,
635 svn_wc_notify_state_t *content_state,
636 svn_wc_notify_state_t *prop_state,
637 const char *mine,
638 const char *older,
639 const char *yours,
640 svn_revnum_t older_rev,
641 svn_revnum_t yours_rev,
642 const char *mimetype1,
643 const char *mimetype2,
644 const apr_array_header_t *prop_changes,
645 apr_hash_t *original_props,
646 void *baton)
648 merge_cmd_baton_t *merge_b = baton;
649 apr_pool_t *subpool = svn_pool_create(merge_b->pool);
650 svn_boolean_t merge_required = TRUE;
651 enum svn_wc_merge_outcome_t merge_outcome;
653 /* Easy out: no access baton means there ain't no merge target */
654 if (adm_access == NULL)
656 if (content_state)
657 *content_state = svn_wc_notify_state_missing;
658 if (prop_state)
659 *prop_state = svn_wc_notify_state_missing;
660 svn_pool_destroy(subpool);
661 return SVN_NO_ERROR;
664 /* Other easy outs: if the merge target isn't under version
665 control, or is just missing from disk, fogettaboutit. There's no
666 way svn_wc_merge3() can do the merge. */
668 const svn_wc_entry_t *entry;
669 svn_node_kind_t kind;
671 SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool));
672 SVN_ERR(svn_io_check_path(mine, &kind, subpool));
674 /* ### a future thought: if the file is under version control,
675 but the working file is missing, maybe we can 'restore' the
676 working file from the text-base, and then allow the merge to run? */
678 if ((! entry) || (kind != svn_node_file))
680 if (content_state)
681 *content_state = svn_wc_notify_state_missing;
682 if (prop_state)
683 *prop_state = svn_wc_notify_state_missing;
684 svn_pool_destroy(subpool);
685 return SVN_NO_ERROR;
689 /* ### TODO: Thwart attempts to merge into a path that has
690 ### unresolved conflicts. This needs to be smart enough to deal
691 ### with tree conflicts!
692 if (is_path_conflicted_by_merge(merge_b, mine))
694 *content_state = svn_wc_notify_state_conflicted;
695 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
696 _("Path '%s' is in conflict, and must be "
697 "resolved before the remainder of the "
698 "requested merge can be applied"), mine);
702 /* This callback is essentially no more than a wrapper around
703 svn_wc_merge3(). Thank goodness that all the
704 diff-editor-mechanisms are doing the hard work of getting the
705 fulltexts! */
707 /* Do property merge before text merge so that keyword expansion takes
708 into account the new property values. */
709 if (prop_changes->nelts > 0)
710 SVN_ERR(merge_props_changed(adm_access, prop_state, mine, prop_changes,
711 original_props, baton));
712 else
713 if (prop_state)
714 *prop_state = svn_wc_notify_state_unchanged;
716 if (older)
718 svn_boolean_t has_local_mods;
719 SVN_ERR(svn_wc_text_modified_p(&has_local_mods, mine, FALSE,
720 adm_access, subpool));
722 /* Special case: if a binary file's working file is
723 exactly identical to the 'left' side of the merge, then don't
724 allow svn_wc_merge to produce a conflict. Instead, just
725 overwrite the working file with the 'right' side of the
726 merge. Why'd we check for local mods above? Because we want
727 to do a different notification depending on whether or not
728 the file was locally modified.
730 Alternately, if the 'left' side of the merge doesn't exist in
731 the repository, and the 'right' side of the merge is
732 identical to the WC, pretend we did the merge (a no-op). */
733 if ((mimetype1 && svn_mime_type_is_binary(mimetype1))
734 || (mimetype2 && svn_mime_type_is_binary(mimetype2)))
736 /* For adds, the 'left' side of the merge doesn't exist. */
737 svn_boolean_t older_revision_exists =
738 !merge_b->add_necessitated_merge;
739 svn_boolean_t same_contents;
740 SVN_ERR(svn_io_files_contents_same_p(&same_contents,
741 (older_revision_exists ?
742 older : yours),
743 mine, subpool));
744 if (same_contents)
746 if (older_revision_exists && !merge_b->dry_run)
747 SVN_ERR(svn_io_file_rename(yours, mine, subpool));
748 merge_outcome = svn_wc_merge_merged;
749 merge_required = FALSE;
753 if (merge_required)
755 /* xgettext: the '.working', '.merge-left.r%ld' and
756 '.merge-right.r%ld' strings are used to tag onto a file
757 name in case of a merge conflict */
758 const char *target_label = _(".working");
759 const char *left_label = apr_psprintf(subpool,
760 _(".merge-left.r%ld"),
761 older_rev);
762 const char *right_label = apr_psprintf(subpool,
763 _(".merge-right.r%ld"),
764 yours_rev);
765 conflict_resolver_baton_t conflict_baton =
766 { merge_b->ctx->conflict_func, merge_b->ctx->conflict_baton,
767 &merge_b->conflicted_paths, merge_b->pool };
768 SVN_ERR(svn_wc_merge3(&merge_outcome,
769 older, yours, mine, adm_access,
770 left_label, right_label, target_label,
771 merge_b->dry_run, merge_b->diff3_cmd,
772 merge_b->merge_options, prop_changes,
773 conflict_resolver, &conflict_baton,
774 subpool));
777 if (content_state)
779 if (merge_outcome == svn_wc_merge_conflict)
780 *content_state = svn_wc_notify_state_conflicted;
781 else if (has_local_mods
782 && merge_outcome != svn_wc_merge_unchanged)
783 *content_state = svn_wc_notify_state_merged;
784 else if (merge_outcome == svn_wc_merge_merged)
785 *content_state = svn_wc_notify_state_changed;
786 else if (merge_outcome == svn_wc_merge_no_merge)
787 *content_state = svn_wc_notify_state_missing;
788 else /* merge_outcome == svn_wc_merge_unchanged */
789 *content_state = svn_wc_notify_state_unchanged;
793 svn_pool_destroy(subpool);
794 return SVN_NO_ERROR;
797 /* A svn_wc_diff_callbacks2_t function. */
798 static svn_error_t *
799 merge_file_added(svn_wc_adm_access_t *adm_access,
800 svn_wc_notify_state_t *content_state,
801 svn_wc_notify_state_t *prop_state,
802 const char *mine,
803 const char *older,
804 const char *yours,
805 svn_revnum_t rev1,
806 svn_revnum_t rev2,
807 const char *mimetype1,
808 const char *mimetype2,
809 const apr_array_header_t *prop_changes,
810 apr_hash_t *original_props,
811 void *baton)
813 merge_cmd_baton_t *merge_b = baton;
814 apr_pool_t *subpool = svn_pool_create(merge_b->pool);
815 svn_node_kind_t kind;
816 const char *copyfrom_url;
817 const char *child;
818 int i;
819 apr_hash_t *new_props;
821 /* In most cases, we just leave prop_state as unknown, and let the
822 content_state what happened, so we set prop_state here to avoid that
823 below. */
824 if (prop_state)
825 *prop_state = svn_wc_notify_state_unknown;
827 /* Apply the prop changes to a new hash table. */
828 new_props = apr_hash_copy(subpool, original_props);
829 for (i = 0; i < prop_changes->nelts; ++i)
831 const svn_prop_t *prop = &APR_ARRAY_IDX(prop_changes, i, svn_prop_t);
832 apr_hash_set(new_props, prop->name, APR_HASH_KEY_STRING, prop->value);
835 /* Easy out: if we have no adm_access for the parent directory,
836 then this portion of the tree-delta "patch" must be inapplicable.
837 Send a 'missing' state back; the repos-diff editor should then
838 send a 'skip' notification. */
839 if (! adm_access)
841 if (merge_b->dry_run && merge_b->added_path
842 && svn_path_is_child(merge_b->added_path, mine, subpool))
844 if (content_state)
845 *content_state = svn_wc_notify_state_changed;
846 if (prop_state && apr_hash_count(new_props))
847 *prop_state = svn_wc_notify_state_changed;
849 else
850 *content_state = svn_wc_notify_state_missing;
851 svn_pool_destroy(subpool);
852 return SVN_NO_ERROR;
855 SVN_ERR(svn_io_check_path(mine, &kind, subpool));
856 switch (kind)
858 case svn_node_none:
860 const svn_wc_entry_t *entry;
861 SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool));
862 if (entry && entry->schedule != svn_wc_schedule_delete)
864 /* It's versioned but missing. */
865 if (content_state)
866 *content_state = svn_wc_notify_state_obstructed;
867 svn_pool_destroy(subpool);
868 return SVN_NO_ERROR;
870 if (! merge_b->dry_run)
872 child = svn_path_is_child(merge_b->target, mine, subpool);
873 if (child != NULL)
874 copyfrom_url = svn_path_url_add_component(merge_b->url, child,
875 subpool);
876 else
877 copyfrom_url = merge_b->url;
878 SVN_ERR(check_scheme_match(adm_access, copyfrom_url));
880 /* Since 'mine' doesn't exist, and this is
881 'merge_file_added', I hope it's safe to assume that
882 'older' is empty, and 'yours' is the full file. Merely
883 copying 'yours' to 'mine', isn't enough; we need to get
884 the whole text-base and props installed too, just as if
885 we had called 'svn cp wc wc'. */
886 SVN_ERR(svn_wc_add_repos_file2(mine, adm_access, yours, NULL,
887 new_props, NULL, copyfrom_url,
888 rev2, subpool));
890 if (content_state)
891 *content_state = svn_wc_notify_state_changed;
892 if (prop_state && apr_hash_count(new_props))
893 *prop_state = svn_wc_notify_state_changed;
895 break;
896 case svn_node_dir:
897 if (content_state)
899 /* directory already exists, is it under version control? */
900 const svn_wc_entry_t *entry;
901 SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool));
903 if (entry && dry_run_deleted_p(merge_b, mine))
904 *content_state = svn_wc_notify_state_changed;
905 else
906 /* this will make the repos_editor send a 'skipped' message */
907 *content_state = svn_wc_notify_state_obstructed;
909 break;
910 case svn_node_file:
912 /* file already exists, is it under version control? */
913 const svn_wc_entry_t *entry;
914 SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool));
916 /* If it's an unversioned file, don't touch it. If it's scheduled
917 for deletion, then rm removed it from the working copy and the
918 user must have recreated it, don't touch it */
919 if (!entry || entry->schedule == svn_wc_schedule_delete)
921 /* this will make the repos_editor send a 'skipped' message */
922 if (content_state)
923 *content_state = svn_wc_notify_state_obstructed;
925 else
927 if (dry_run_deleted_p(merge_b, mine))
929 if (content_state)
930 *content_state = svn_wc_notify_state_changed;
932 else
934 /* Indicate that we merge because of an add to handle a
935 special case for binary files with no local mods. */
936 merge_b->add_necessitated_merge = TRUE;
938 SVN_ERR(merge_file_changed(adm_access, content_state,
939 prop_state, mine, older, yours,
940 rev1, rev2,
941 mimetype1, mimetype2,
942 prop_changes, original_props,
943 baton));
945 /* Reset the state so that the baton can safely be reused
946 in subsequent ops occurring during this merge. */
947 merge_b->add_necessitated_merge = FALSE;
950 break;
952 default:
953 if (content_state)
954 *content_state = svn_wc_notify_state_unknown;
955 break;
958 svn_pool_destroy(subpool);
959 return SVN_NO_ERROR;
962 /* A svn_wc_diff_callbacks2_t function. */
963 static svn_error_t *
964 merge_file_deleted(svn_wc_adm_access_t *adm_access,
965 svn_wc_notify_state_t *state,
966 const char *mine,
967 const char *older,
968 const char *yours,
969 const char *mimetype1,
970 const char *mimetype2,
971 apr_hash_t *original_props,
972 void *baton)
974 merge_cmd_baton_t *merge_b = baton;
975 apr_pool_t *subpool = svn_pool_create(merge_b->pool);
976 svn_node_kind_t kind;
977 svn_wc_adm_access_t *parent_access;
978 const char *parent_path;
979 svn_error_t *err;
981 /* Easy out: if we have no adm_access for the parent directory,
982 then this portion of the tree-delta "patch" must be inapplicable.
983 Send a 'missing' state back; the repos-diff editor should then
984 send a 'skip' notification. */
985 if (! adm_access)
987 if (state)
988 *state = svn_wc_notify_state_missing;
989 svn_pool_destroy(subpool);
990 return SVN_NO_ERROR;
993 SVN_ERR(svn_io_check_path(mine, &kind, subpool));
994 switch (kind)
996 case svn_node_file:
997 svn_path_split(mine, &parent_path, NULL, subpool);
998 SVN_ERR(svn_wc_adm_retrieve(&parent_access, adm_access, parent_path,
999 subpool));
1000 /* Passing NULL for the notify_func and notify_baton because
1001 repos_diff.c:delete_entry() will do it for us. */
1002 err = svn_client__wc_delete(mine, parent_access, merge_b->force,
1003 merge_b->dry_run, FALSE, NULL, NULL,
1004 merge_b->ctx, subpool);
1005 if (err && state)
1007 *state = svn_wc_notify_state_obstructed;
1008 svn_error_clear(err);
1010 else if (state)
1012 *state = svn_wc_notify_state_changed;
1014 break;
1015 case svn_node_dir:
1016 if (state)
1017 *state = svn_wc_notify_state_obstructed;
1018 break;
1019 case svn_node_none:
1020 /* file is already non-existent, this is a no-op. */
1021 if (state)
1022 *state = svn_wc_notify_state_missing;
1023 break;
1024 default:
1025 if (state)
1026 *state = svn_wc_notify_state_unknown;
1027 break;
1030 svn_pool_destroy(subpool);
1031 return SVN_NO_ERROR;
1034 /* A svn_wc_diff_callbacks2_t function. */
1035 static svn_error_t *
1036 merge_dir_added(svn_wc_adm_access_t *adm_access,
1037 svn_wc_notify_state_t *state,
1038 const char *path,
1039 svn_revnum_t rev,
1040 void *baton)
1042 merge_cmd_baton_t *merge_b = baton;
1043 apr_pool_t *subpool = svn_pool_create(merge_b->pool);
1044 svn_node_kind_t kind;
1045 const svn_wc_entry_t *entry;
1046 const char *copyfrom_url, *child;
1048 /* Easy out: if we have no adm_access for the parent directory,
1049 then this portion of the tree-delta "patch" must be inapplicable.
1050 Send a 'missing' state back; the repos-diff editor should then
1051 send a 'skip' notification. */
1052 if (! adm_access)
1054 if (state)
1056 if (merge_b->dry_run && merge_b->added_path
1057 && svn_path_is_child(merge_b->added_path, path, subpool))
1058 *state = svn_wc_notify_state_changed;
1059 else
1060 *state = svn_wc_notify_state_missing;
1062 svn_pool_destroy(subpool);
1063 return SVN_NO_ERROR;
1066 child = svn_path_is_child(merge_b->target, path, subpool);
1067 assert(child != NULL);
1068 copyfrom_url = svn_path_url_add_component(merge_b->url, child, subpool);
1069 SVN_ERR(check_scheme_match(adm_access, copyfrom_url));
1071 SVN_ERR(svn_io_check_path(path, &kind, subpool));
1072 switch (kind)
1074 case svn_node_none:
1075 SVN_ERR(svn_wc_entry(&entry, path, adm_access, FALSE, subpool));
1076 if (entry && entry->schedule != svn_wc_schedule_delete)
1078 /* Versioned but missing */
1079 if (state)
1080 *state = svn_wc_notify_state_obstructed;
1081 svn_pool_destroy(subpool);
1082 return SVN_NO_ERROR;
1084 if (merge_b->dry_run)
1085 merge_b->added_path = apr_pstrdup(merge_b->pool, path);
1086 else
1088 SVN_ERR(svn_io_make_dir_recursively(path, subpool));
1089 SVN_ERR(svn_wc_add2(path, adm_access,
1090 copyfrom_url, rev,
1091 merge_b->ctx->cancel_func,
1092 merge_b->ctx->cancel_baton,
1093 NULL, NULL, /* don't pass notification func! */
1094 subpool));
1097 if (state)
1098 *state = svn_wc_notify_state_changed;
1099 break;
1100 case svn_node_dir:
1101 /* Adding an unversioned directory doesn't destroy data */
1102 SVN_ERR(svn_wc_entry(&entry, path, adm_access, TRUE, subpool));
1103 if (! entry || entry->schedule == svn_wc_schedule_delete)
1105 if (!merge_b->dry_run)
1106 SVN_ERR(svn_wc_add2(path, adm_access,
1107 copyfrom_url, rev,
1108 merge_b->ctx->cancel_func,
1109 merge_b->ctx->cancel_baton,
1110 NULL, NULL, /* no notification func! */
1111 subpool));
1112 else
1113 merge_b->added_path = apr_pstrdup(merge_b->pool, path);
1114 if (state)
1115 *state = svn_wc_notify_state_changed;
1117 else if (state)
1119 if (dry_run_deleted_p(merge_b, path))
1120 *state = svn_wc_notify_state_changed;
1121 else
1122 *state = svn_wc_notify_state_obstructed;
1124 break;
1125 case svn_node_file:
1126 if (merge_b->dry_run)
1127 merge_b->added_path = NULL;
1129 if (state)
1131 SVN_ERR(svn_wc_entry(&entry, path, adm_access, FALSE, subpool));
1133 if (entry && dry_run_deleted_p(merge_b, path))
1134 /* ### TODO: Retain record of this dir being added to
1135 ### avoid problems from subsequent edits which try to
1136 ### add children. */
1137 *state = svn_wc_notify_state_changed;
1138 else
1139 *state = svn_wc_notify_state_obstructed;
1141 break;
1142 default:
1143 if (merge_b->dry_run)
1144 merge_b->added_path = NULL;
1145 if (state)
1146 *state = svn_wc_notify_state_unknown;
1147 break;
1150 svn_pool_destroy(subpool);
1151 return SVN_NO_ERROR;
1154 /* A svn_wc_diff_callbacks2_t function. */
1155 static svn_error_t *
1156 merge_dir_deleted(svn_wc_adm_access_t *adm_access,
1157 svn_wc_notify_state_t *state,
1158 const char *path,
1159 void *baton)
1161 merge_cmd_baton_t *merge_b = baton;
1162 apr_pool_t *subpool = svn_pool_create(merge_b->pool);
1163 svn_node_kind_t kind;
1164 svn_wc_adm_access_t *parent_access;
1165 const char *parent_path;
1166 svn_error_t *err;
1168 /* Easy out: if we have no adm_access for the parent directory,
1169 then this portion of the tree-delta "patch" must be inapplicable.
1170 Send a 'missing' state back; the repos-diff editor should then
1171 send a 'skip' notification. */
1172 if (! adm_access)
1174 if (state)
1175 *state = svn_wc_notify_state_missing;
1176 svn_pool_destroy(subpool);
1177 return SVN_NO_ERROR;
1180 SVN_ERR(svn_io_check_path(path, &kind, subpool));
1181 switch (kind)
1183 case svn_node_dir:
1185 svn_path_split(path, &parent_path, NULL, subpool);
1186 SVN_ERR(svn_wc_adm_retrieve(&parent_access, adm_access, parent_path,
1187 subpool));
1188 /* Passing NULL for the notify_func and notify_baton because
1189 repos_diff.c:delete_entry() will do it for us. */
1190 err = svn_client__wc_delete(path, parent_access, merge_b->force,
1191 merge_b->dry_run, FALSE,
1192 NULL, NULL,
1193 merge_b->ctx, subpool);
1194 if (err && state)
1196 *state = svn_wc_notify_state_obstructed;
1197 svn_error_clear(err);
1199 else if (state)
1201 *state = svn_wc_notify_state_changed;
1204 break;
1205 case svn_node_file:
1206 if (state)
1207 *state = svn_wc_notify_state_obstructed;
1208 break;
1209 case svn_node_none:
1210 /* dir is already non-existent, this is a no-op. */
1211 if (state)
1212 *state = svn_wc_notify_state_missing;
1213 break;
1214 default:
1215 if (state)
1216 *state = svn_wc_notify_state_unknown;
1217 break;
1220 svn_pool_destroy(subpool);
1221 return SVN_NO_ERROR;
1224 /* The main callback table for 'svn merge'. */
1225 static const svn_wc_diff_callbacks2_t
1226 merge_callbacks =
1228 merge_file_changed,
1229 merge_file_added,
1230 merge_file_deleted,
1231 merge_dir_added,
1232 merge_dir_deleted,
1233 merge_props_changed
1237 /*-----------------------------------------------------------------------*/
1239 /*** Merge Notification ***/
1242 /* Contains any state collected while receiving path notifications. */
1243 typedef struct
1245 /* The wrapped callback and baton. */
1246 svn_wc_notify_func2_t wrapped_func;
1247 void *wrapped_baton;
1249 /* The number of notifications received. */
1250 apr_uint32_t nbr_notifications;
1252 /* The number of operative notifications received. */
1253 apr_uint32_t nbr_operative_notifications;
1255 /* The list of merged paths. */
1256 apr_hash_t *merged_paths;
1258 /* The list of any skipped paths, which should be examined and
1259 cleared after each invocation of the callback. */
1260 apr_hash_t *skipped_paths;
1262 /* A list of the root paths of any added subtrees which might require
1263 their own explicit mergeinfo. */
1264 apr_hash_t *added_paths;
1266 /* Flag indicating whether it is a single file merge or not. */
1267 svn_boolean_t is_single_file_merge;
1269 /* Depth first ordered list of paths that needs special care while merging.
1270 This defaults to NULL. For 'same_url' merge alone we set it to
1271 proper array. This is used by notification_receiver to put a
1272 merge notification begin lines. */
1273 apr_array_header_t *children_with_mergeinfo;
1275 /* The index in CHILDREN_WITH_MERGEINFO where we found the nearest ancestor
1276 for merged path. Default value is '-1'.*/
1277 int cur_ancestor_index;
1279 /* We use this to make a decision on merge begin line notifications. */
1280 merge_cmd_baton_t *merge_b;
1282 /* Pool used in notification_receiver() to avoid the iteration
1283 sub-pool which is passed in, then subsequently destroyed. */
1284 apr_pool_t *pool;
1286 } notification_receiver_baton_t;
1289 /* Finds a nearest ancestor in CHILDREN_WITH_MERGEINFO for PATH.
1290 CHILDREN_WITH_MERGEINFO is expected to be sorted in Depth first
1291 order of path. Nearest ancestor's index from
1292 CHILDREN_WITH_MERGEINFO is returned. */
1293 static int
1294 find_nearest_ancestor(apr_array_header_t *children_with_mergeinfo,
1295 const char *path)
1297 int i;
1298 int ancestor_index = 0;
1300 /* This if condition is not needed as this function should be used
1301 from the context of same_url merge where CHILDREN_WITH_MERGEINFO
1302 will not be NULL and of size atleast 1. We have this if condition
1303 just to protect the wrong caller. */
1304 if (!children_with_mergeinfo)
1305 return 0;
1306 for (i = 0; i < children_with_mergeinfo->nelts; i++)
1308 svn_client__merge_path_t *child =
1309 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
1310 if (svn_path_is_ancestor(child->path, path))
1311 ancestor_index = i;
1313 return ancestor_index;
1317 #define IS_OPERATIVE_NOTIFICATION(notify) \
1318 (notify->content_state == svn_wc_notify_state_conflicted \
1319 || notify->content_state == svn_wc_notify_state_merged \
1320 || notify->content_state == svn_wc_notify_state_changed \
1321 || notify->prop_state == svn_wc_notify_state_conflicted \
1322 || notify->prop_state == svn_wc_notify_state_merged \
1323 || notify->prop_state == svn_wc_notify_state_changed \
1324 || notify->action == svn_wc_notify_update_add)
1326 /* Our svn_wc_notify_func2_t wrapper.*/
1327 static void
1328 notification_receiver(void *baton, const svn_wc_notify_t *notify,
1329 apr_pool_t *pool)
1331 notification_receiver_baton_t *notify_b = baton;
1332 svn_boolean_t is_operative_notification = FALSE;
1334 /* Is the notification the result of a real operative merge? */
1335 if (IS_OPERATIVE_NOTIFICATION(notify))
1337 notify_b->nbr_operative_notifications++;
1338 is_operative_notification = TRUE;
1341 /* If our merge sources are ancestors of one another... */
1342 if (notify_b->merge_b->sources_ancestral)
1344 notify_b->nbr_notifications++;
1346 /* See if this is an operative directory merge. */
1347 if (!(notify_b->is_single_file_merge) && is_operative_notification)
1349 int new_nearest_ancestor_index =
1350 find_nearest_ancestor(notify_b->children_with_mergeinfo,
1351 notify->path);
1352 if (new_nearest_ancestor_index != notify_b->cur_ancestor_index)
1354 svn_client__merge_path_t *child =
1355 APR_ARRAY_IDX(notify_b->children_with_mergeinfo,
1356 new_nearest_ancestor_index,
1357 svn_client__merge_path_t *);
1358 notify_b->cur_ancestor_index = new_nearest_ancestor_index;
1359 if (!child->absent && child->remaining_ranges->nelts > 0
1360 && !(new_nearest_ancestor_index == 0
1361 && notify_b->merge_b->target_has_dummy_merge_range))
1363 svn_wc_notify_t *notify_merge_begin;
1364 notify_merge_begin =
1365 svn_wc_create_notify(child->path,
1366 svn_wc_notify_merge_begin, pool);
1367 notify_merge_begin->merge_range =
1368 APR_ARRAY_IDX(child->remaining_ranges, 0,
1369 svn_merge_range_t *);
1370 if (notify_b->wrapped_func)
1371 (*notify_b->wrapped_func)(notify_b->wrapped_baton,
1372 notify_merge_begin, pool);
1377 if (notify->content_state == svn_wc_notify_state_merged
1378 || notify->content_state == svn_wc_notify_state_changed
1379 || notify->prop_state == svn_wc_notify_state_merged
1380 || notify->prop_state == svn_wc_notify_state_changed
1381 || notify->action == svn_wc_notify_update_add)
1383 const char *merged_path = apr_pstrdup(notify_b->pool, notify->path);
1385 if (notify_b->merged_paths == NULL)
1386 notify_b->merged_paths = apr_hash_make(notify_b->pool);
1388 apr_hash_set(notify_b->merged_paths, merged_path,
1389 APR_HASH_KEY_STRING, merged_path);
1392 if (notify->action == svn_wc_notify_skip)
1394 const char *skipped_path = apr_pstrdup(notify_b->pool, notify->path);
1396 if (notify_b->skipped_paths == NULL)
1397 notify_b->skipped_paths = apr_hash_make(notify_b->pool);
1399 apr_hash_set(notify_b->skipped_paths, skipped_path,
1400 APR_HASH_KEY_STRING, skipped_path);
1402 if (notify->action == svn_wc_notify_update_add)
1404 svn_boolean_t is_root_of_added_subtree = FALSE;
1405 const char *added_path = apr_pstrdup(notify_b->pool, notify->path);
1406 const char *added_path_parent = NULL;
1408 /* Stash the root path of any added subtrees. */
1409 if (notify_b->added_paths == NULL)
1411 notify_b->added_paths = apr_hash_make(notify_b->pool);
1412 is_root_of_added_subtree = TRUE;
1414 else
1416 added_path_parent = svn_path_dirname(added_path, pool);
1417 if (!apr_hash_get(notify_b->added_paths, added_path_parent,
1418 APR_HASH_KEY_STRING))
1419 is_root_of_added_subtree = TRUE;
1421 if (is_root_of_added_subtree)
1422 apr_hash_set(notify_b->added_paths, added_path,
1423 APR_HASH_KEY_STRING, added_path);
1426 /* Otherwise, our merge sources aren't ancestors of one another. */
1427 else if (!(notify_b->is_single_file_merge)
1428 && notify_b->nbr_operative_notifications == 1
1429 && is_operative_notification)
1431 svn_wc_notify_t *notify_merge_begin;
1432 notify_merge_begin = svn_wc_create_notify(notify_b->merge_b->target,
1433 svn_wc_notify_merge_begin,
1434 pool);
1435 if (notify_b->wrapped_func)
1436 (*notify_b->wrapped_func)(notify_b->wrapped_baton, notify_merge_begin,
1437 pool);
1440 if (notify_b->wrapped_func)
1441 (*notify_b->wrapped_func)(notify_b->wrapped_baton, notify, pool);
1445 /*-----------------------------------------------------------------------*/
1447 /*** Determining What Remains To Be Merged ***/
1449 /* Calculate a rangelist of svn_merge_range_t *'s -- for use by
1450 drive_merge_report_editor()'s application of the editor to the WC
1451 -- by subtracting revisions which have already been merged from
1452 MERGEINFO_PATH into the working copy from the requested range(s)
1453 REQUESTED_MERGE, and storing what's left in REMAINING_RANGES.
1454 TARGET_MERGEINFO may be NULL.
1456 NOTE: This should only be called when honoring mergeinfo.
1458 static svn_error_t *
1459 filter_merged_revisions(apr_array_header_t **remaining_ranges,
1460 const char *mergeinfo_path,
1461 svn_mergeinfo_t target_mergeinfo,
1462 svn_mergeinfo_t implicit_mergeinfo,
1463 apr_array_header_t *requested_merge,
1464 svn_boolean_t is_rollback,
1465 const svn_wc_entry_t *entry,
1466 apr_pool_t *pool)
1468 apr_array_header_t *target_rangelist = NULL;
1469 svn_mergeinfo_t mergeinfo;
1471 if (is_rollback)
1473 mergeinfo = svn_mergeinfo_dup(implicit_mergeinfo, pool);
1474 if (target_mergeinfo)
1475 SVN_ERR(svn_mergeinfo_merge(mergeinfo, target_mergeinfo, pool));
1476 target_rangelist = apr_hash_get(mergeinfo,
1477 mergeinfo_path, APR_HASH_KEY_STRING);
1478 if (target_rangelist)
1480 /* Return the intersection of the revs which are both
1481 already represented by the WC and are requested for
1482 revert. The revert range and will need to be reversed
1483 for our APIs to work properly, as will the output for the
1484 revert to work properly. */
1485 requested_merge = svn_rangelist_dup(requested_merge, pool);
1486 SVN_ERR(svn_rangelist_reverse(requested_merge, pool));
1487 SVN_ERR(svn_rangelist_intersect(remaining_ranges,
1488 target_rangelist,
1489 requested_merge, pool));
1490 SVN_ERR(svn_rangelist_reverse(*remaining_ranges, pool));
1492 else
1494 *remaining_ranges =
1495 apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
1498 else
1500 *remaining_ranges = requested_merge;
1502 /* ### TODO: Which evil shall we choose?
1504 ### If we allow all forward-merges not already found in recorded
1505 ### mergeinfo, we destroy the ability to, say, merge the whole of a
1506 ### branch to the trunk while automatically ignoring the revisions
1507 ### common to both. That's bad.
1509 ### If we allow only forward-merges not found in either recorded
1510 ### mergeinfo or implicit mergeinfo (natural history), then the
1511 ### previous scenario works great, but we can't reverse-merge a
1512 ### previous change made to our line of history and then remake it
1513 ### (because the reverse-merge will leave no mergeinfo trace, and
1514 ### the remake-it attempt will still find the original change in
1515 ### natural mergeinfo. But you know, that we happen to use 'merge'
1516 ### for revision undoing is somewhat unnatural anyway, so I'm
1517 ### finding myself having little interest in caring too much about
1518 ### this. That said, if we had a way of storing reverse merge
1519 ### ranges, we'd be in good shape either way.
1521 #ifdef SVN_MERGE__ALLOW_ALL_FORWARD_MERGES_FROM_SELF
1522 if (target_mergeinfo)
1523 target_rangelist = apr_hash_get(target_mergeinfo,
1524 mergeinfo_path, APR_HASH_KEY_STRING);
1525 #else
1526 mergeinfo = svn_mergeinfo_dup(implicit_mergeinfo, pool);
1527 if (target_mergeinfo)
1528 SVN_ERR(svn_mergeinfo_merge(mergeinfo, target_mergeinfo, pool));
1529 target_rangelist = apr_hash_get(mergeinfo,
1530 mergeinfo_path, APR_HASH_KEY_STRING);
1531 #endif
1533 if (target_rangelist)
1534 SVN_ERR(svn_rangelist_remove(remaining_ranges, target_rangelist,
1535 requested_merge, FALSE, pool));
1537 return SVN_NO_ERROR;
1540 /* Populate *REMAINING_RANGES with a list of revision ranges
1541 constructed by taking after removing already-merged ranges.
1542 Cascade SOURCE_ROOT_URL, URL1, REVISION1, URL2, REVISION2, INHERITABLE,
1543 TARGET_MERGEINFO, IMPLICIT_MERGEINFO, RA_SESSION, ENTRY, CTX, and POOL.
1545 See `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements
1546 around the values of URL1, REVISION1, URL2, and REVISION2.
1548 NOTE: This should only be called when honoring mergeinfo.
1550 static svn_error_t *
1551 calculate_remaining_ranges(apr_array_header_t **remaining_ranges,
1552 const char *source_root_url,
1553 const char *url1,
1554 svn_revnum_t revision1,
1555 const char *url2,
1556 svn_revnum_t revision2,
1557 svn_boolean_t inheritable,
1558 svn_mergeinfo_t target_mergeinfo,
1559 svn_mergeinfo_t implicit_mergeinfo,
1560 svn_ra_session_t *ra_session,
1561 const svn_wc_entry_t *entry,
1562 svn_client_ctx_t *ctx,
1563 apr_pool_t *pool)
1565 apr_array_header_t *requested_rangelist;
1566 const char *mergeinfo_path;
1567 svn_merge_range_t *range = apr_pcalloc(pool, sizeof(*range));
1568 const char *primary_url = (revision1 < revision2) ? url2 : url1;
1570 /* Determine which of the requested ranges to consider merging... */
1571 requested_rangelist = apr_array_make(pool, 1, sizeof(range));
1572 range->start = revision1;
1573 range->end = revision2;
1574 range->inheritable = inheritable;
1575 APR_ARRAY_PUSH(requested_rangelist, svn_merge_range_t *) = range;
1577 /* ...and of those ranges, determine which ones actually still
1578 need merging. */
1579 SVN_ERR(svn_client__path_relative_to_root(&mergeinfo_path, primary_url,
1580 source_root_url, TRUE,
1581 ra_session, NULL, pool));
1582 SVN_ERR(filter_merged_revisions(remaining_ranges, mergeinfo_path,
1583 target_mergeinfo, implicit_mergeinfo,
1584 requested_rangelist,
1585 (revision1 > revision2), entry, pool));
1586 return SVN_NO_ERROR;
1590 static svn_error_t *
1591 get_full_mergeinfo(svn_mergeinfo_t *recorded_mergeinfo,
1592 svn_mergeinfo_t *implicit_mergeinfo,
1593 const svn_wc_entry_t *entry,
1594 svn_boolean_t *indirect,
1595 svn_mergeinfo_inheritance_t inherit,
1596 svn_ra_session_t *ra_session,
1597 const char *target_wcpath,
1598 svn_revnum_t start,
1599 svn_revnum_t end,
1600 svn_wc_adm_access_t *adm_access,
1601 svn_client_ctx_t *ctx,
1602 apr_pool_t *pool)
1604 const char *session_url = NULL, *url;
1605 svn_revnum_t target_rev;
1606 svn_opt_revision_t peg_revision;
1607 apr_pool_t *sesspool = NULL;
1609 /* Assert that we have sane input. */
1610 assert(SVN_IS_VALID_REVNUM(start)
1611 && SVN_IS_VALID_REVNUM(end)
1612 && (start > end));
1614 /* First, we get the real mergeinfo. */
1615 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(recorded_mergeinfo, entry,
1616 indirect, FALSE, inherit,
1617 ra_session, target_wcpath,
1618 adm_access, ctx, pool));
1620 peg_revision.kind = svn_opt_revision_working;
1621 SVN_ERR(svn_client__derive_location(&url, &target_rev, target_wcpath,
1622 &peg_revision, ra_session, adm_access,
1623 ctx, pool));
1624 if (target_rev <= end)
1626 /* We're asking about a range outside our natural history
1627 altogether. That means our implicit mergeinfo is empty. */
1628 *implicit_mergeinfo = apr_hash_make(pool);
1629 return SVN_NO_ERROR;
1632 /* Temporarily point our RA_SESSION at our target URL so we can
1633 fetch so-called "implicit mergeinfo" (that is, natural history). */
1634 if (ra_session)
1636 SVN_ERR(svn_client__ensure_ra_session_url(&session_url, ra_session,
1637 url, pool));
1639 else
1641 sesspool = svn_pool_create(pool);
1642 SVN_ERR(svn_client__open_ra_session_internal(&ra_session, url,
1643 NULL, NULL, NULL, FALSE,
1644 TRUE, ctx, sesspool));
1647 /* Our underlying APIs can't yet handle the case where the peg
1648 revision isn't the youngest of the three revisions. So we'll
1649 just verify that the source in the peg revision is related to the
1650 the source in the youngest requested revision (which is all the
1651 underlying APIs would do in this case right now anyway). */
1652 if (target_rev < start)
1654 const char *start_url;
1655 svn_opt_revision_t requested, unspec, pegrev, *start_revision;
1656 unspec.kind = svn_opt_revision_unspecified;
1657 requested.kind = svn_opt_revision_number;
1658 requested.value.number = start;
1659 pegrev.kind = svn_opt_revision_number;
1660 pegrev.value.number = target_rev;
1662 SVN_ERR(svn_client__repos_locations(&start_url, &start_revision,
1663 NULL, NULL, ra_session, url,
1664 &pegrev, &requested,
1665 &unspec, ctx, pool));
1666 /* ### FIXME: Having a low-brain moment. Shouldn't we check
1667 that START_URL matches our session URL at this point? */
1668 target_rev = start;
1671 /* Fetch the implicit mergeinfo. */
1672 peg_revision.kind = svn_opt_revision_number;
1673 peg_revision.value.number = target_rev;
1674 SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo, url,
1675 &peg_revision, start, end,
1676 ra_session, NULL, ctx, pool));
1678 /* If we created an RA_SESSION above, destroy it. Otherwise, if
1679 reparented an existing session, point it back where it was when
1680 we were called. */
1681 if (sesspool)
1683 svn_pool_destroy(sesspool);
1685 else if (session_url)
1687 SVN_ERR(svn_ra_reparent(ra_session, session_url, pool));
1690 return SVN_NO_ERROR;
1694 /* For each child in CHILDREN_WITH_MERGEINFO, it populates that
1695 child's remaining_ranges list. CHILDREN_WITH_MERGEINFO is expected
1696 to be sorted in depth first order. All persistent allocations are
1697 from CHILDREN_WITH_MERGEINFO->pool.
1699 If HONOR_MERGEINFO is set, this function will actually try to be
1700 intelligent about populating remaining_ranges list. Otherwise, it
1701 will claim that each child has a single remaining range, from
1702 revision1, to revision2.
1704 See `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements
1705 around the values of URL1, REVISION1, URL2, and REVISION2.
1707 static svn_error_t *
1708 populate_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
1709 const char *source_root_url,
1710 const char *url1,
1711 svn_revnum_t revision1,
1712 const char *url2,
1713 svn_revnum_t revision2,
1714 svn_boolean_t inheritable,
1715 svn_boolean_t honor_mergeinfo,
1716 svn_ra_session_t *ra_session,
1717 const char *parent_merge_src_canon_path,
1718 svn_wc_adm_access_t *adm_access,
1719 merge_cmd_baton_t *merge_b)
1721 apr_pool_t *iterpool, *pool;
1722 int merge_target_len = strlen(merge_b->target);
1723 int i;
1725 pool = children_with_mergeinfo->pool;
1726 iterpool = svn_pool_create(pool);
1728 /* If we aren't honoring mergeinfo, we'll make quick work of this by
1729 simply adding dummy REVISION1:REVISION2 ranges for all children. */
1730 if (! honor_mergeinfo)
1732 for (i = 0; i < children_with_mergeinfo->nelts; i++)
1734 svn_client__merge_path_t *child =
1735 APR_ARRAY_IDX(children_with_mergeinfo, i,
1736 svn_client__merge_path_t *);
1737 svn_merge_range_t *range = apr_pcalloc(pool, sizeof(*range));
1739 range->start = revision1;
1740 range->end = revision2;
1741 range->inheritable = inheritable;
1743 child->remaining_ranges =
1744 apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
1745 APR_ARRAY_PUSH(child->remaining_ranges, svn_merge_range_t *) = range;
1747 return SVN_NO_ERROR;
1750 for (i = 0; i < children_with_mergeinfo->nelts; i++)
1752 const char *child_repos_path;
1753 const svn_wc_entry_t *child_entry;
1754 const char *child_url1, *child_url2;
1755 svn_mergeinfo_t implicit_mergeinfo;
1756 svn_client__merge_path_t *child =
1757 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
1759 /* If the path is absent don't do subtree merge either. */
1760 if (!child || child->absent)
1761 continue;
1763 svn_pool_clear(iterpool);
1765 if (strlen(child->path) == merge_target_len)
1766 child_repos_path = "";
1767 else
1768 child_repos_path = child->path +
1769 (merge_target_len ? merge_target_len + 1 : 0);
1770 child_url1 = svn_path_join(url1, child_repos_path, iterpool);
1771 child_url2 = svn_path_join(url2, child_repos_path, iterpool);
1773 SVN_ERR(svn_wc__entry_versioned(&child_entry, child->path, adm_access,
1774 FALSE, iterpool));
1776 SVN_ERR(get_full_mergeinfo(&(child->pre_merge_mergeinfo),
1777 &implicit_mergeinfo, child_entry,
1778 &(child->indirect_mergeinfo),
1779 svn_mergeinfo_inherited, NULL, child->path,
1780 MAX(revision1, revision2),
1781 MIN(revision1, revision2),
1782 adm_access, merge_b->ctx, pool));
1784 SVN_ERR(calculate_remaining_ranges(&(child->remaining_ranges),
1785 source_root_url,
1786 child_url1, revision1,
1787 child_url2, revision2,
1788 inheritable,
1789 child->pre_merge_mergeinfo,
1790 implicit_mergeinfo,
1791 ra_session, child_entry, merge_b->ctx,
1792 pool));
1795 /* Take advantage of the depth first ordering,
1796 i.e first(0th) item is target.*/
1797 if (children_with_mergeinfo->nelts > 1)
1799 svn_client__merge_path_t *child =
1800 APR_ARRAY_IDX(children_with_mergeinfo, 0, svn_client__merge_path_t *);
1802 if (child->remaining_ranges->nelts == 0)
1804 svn_merge_range_t *dummy_range =
1805 apr_pcalloc(pool, sizeof(*dummy_range));
1806 dummy_range->start = revision2;
1807 dummy_range->end = revision2;
1808 dummy_range->inheritable = inheritable;
1809 child->remaining_ranges = apr_array_make(pool, 1,
1810 sizeof(dummy_range));
1811 APR_ARRAY_PUSH(child->remaining_ranges, svn_merge_range_t *) =
1812 dummy_range;
1813 merge_b->target_has_dummy_merge_range = TRUE;
1817 svn_pool_destroy(iterpool);
1818 return SVN_NO_ERROR;
1822 /*-----------------------------------------------------------------------*/
1824 /*** Other Helper Functions ***/
1827 /* Create mergeinfo describing the merge of RANGE into our target, accounting
1828 for paths unaffected by the merge due to skips or conflicts from
1829 NOTIFY_B. For 'immediates' merge it sets an inheritable mergeinfo
1830 corresponding to current merge on merge target. For 'files' merge it sets
1831 an inheritable mergeinfo corrsponding to current merge on merged files.
1832 Note in MERGE_B->OPERATIVE_MERGE if an operative merge
1833 is discovered. If TARGET_WCPATH is a directory and it is missing
1834 an immediate child then TARGET_MISSING_CHILD should be true,
1835 otherwise it is false.*/
1836 static svn_error_t *
1837 determine_merges_performed(apr_hash_t **merges, const char *target_wcpath,
1838 svn_merge_range_t *range,
1839 svn_depth_t depth,
1840 svn_wc_adm_access_t *adm_access,
1841 notification_receiver_baton_t *notify_b,
1842 merge_cmd_baton_t *merge_b,
1843 apr_pool_t *pool)
1845 apr_array_header_t *rangelist;
1846 apr_size_t nbr_skips = (notify_b->skipped_paths != NULL ?
1847 apr_hash_count(notify_b->skipped_paths) : 0);
1848 *merges = apr_hash_make(pool);
1850 /* If a merge is ultimately a no-op we don't update the mergeinfo (see
1851 issue #2883). But we still determine the merges "performed" for purposes
1852 of updating the mergeinfo right now (at least temporarily), because at
1853 this point we don't know if there are multiple merge sources yet to be
1854 applied, some of which could be operative (see issue #2977). */
1855 if (notify_b->nbr_operative_notifications > 0)
1856 merge_b->operative_merge = TRUE;
1857 else
1859 /* If TARGET_WCPATH didn't have working mergeinfo at the start of
1860 the merge make a note of that in our hash of working mergeinfo
1861 prior to the merge. */
1862 if (!apr_hash_get(merge_b->working_mergeinfo, target_wcpath,
1863 APR_HASH_KEY_STRING))
1865 working_mergeinfo_t *working_mergeinfo =
1866 apr_pcalloc(merge_b->long_pool, sizeof(*working_mergeinfo));
1867 apr_hash_set(merge_b->working_mergeinfo, target_wcpath,
1868 APR_HASH_KEY_STRING, working_mergeinfo);
1872 rangelist = apr_array_make(pool, 1, sizeof(range));
1873 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = range;
1874 apr_hash_set(*merges, target_wcpath, APR_HASH_KEY_STRING, rangelist);
1875 if (nbr_skips > 0)
1877 apr_hash_index_t *hi;
1879 /* Override the mergeinfo for child paths which weren't
1880 actually merged. */
1881 for (hi = apr_hash_first(NULL, notify_b->skipped_paths); hi;
1882 hi = apr_hash_next(hi))
1884 const void *skipped_path;
1885 svn_wc_status2_t *status;
1887 apr_hash_this(hi, &skipped_path, NULL, NULL);
1889 /* Before we override, make sure this is a versioned path, it
1890 might be an unversioned obstruction. */
1891 SVN_ERR(svn_wc_status2(&status, (const char *) skipped_path,
1892 adm_access, pool));
1893 if (status->text_status == svn_wc_status_none
1894 || status->text_status == svn_wc_status_unversioned)
1895 continue;
1897 /* Add an empty range list for this path.
1899 ### TODO: This works fine for a file path skipped because it is
1900 ### missing as long as the file's parent directory is present.
1901 ### But missing directory paths skipped are not handled yet,
1902 ### see issue #2915. */
1903 apr_hash_set(*merges, (const char *) skipped_path,
1904 APR_HASH_KEY_STRING,
1905 apr_array_make(pool, 0, sizeof(range)));
1907 /* If SKIPPED_PATH didn't have working mergeinfo at the start of the
1908 merge make a note of that too. */
1909 if (!apr_hash_get(merge_b->working_mergeinfo,
1910 (const char *) skipped_path,
1911 APR_HASH_KEY_STRING))
1913 working_mergeinfo_t *working_mergeinfo
1914 = apr_pcalloc(merge_b->long_pool,
1915 sizeof(*working_mergeinfo));
1916 apr_hash_set(merge_b->working_mergeinfo,
1917 (const char *) skipped_path,
1918 APR_HASH_KEY_STRING, working_mergeinfo);
1921 if (nbr_skips < notify_b->nbr_notifications)
1922 /* ### Use RANGELIST as the mergeinfo for all children of
1923 ### this path which were not also explicitly
1924 ### skipped? */
1928 if ((depth != svn_depth_infinity) && notify_b->merged_paths)
1930 apr_hash_index_t *hi;
1931 const void *merged_path;
1933 for (hi = apr_hash_first(NULL, notify_b->merged_paths); hi;
1934 hi = apr_hash_next(hi))
1936 const svn_wc_entry_t *child_entry;
1937 svn_merge_range_t *child_merge_range;
1938 apr_array_header_t *rangelist_of_child = NULL;
1939 apr_hash_this(hi, &merged_path, NULL, NULL);
1940 child_merge_range = svn_merge_range_dup(range, pool);
1941 SVN_ERR(svn_wc__entry_versioned(&child_entry,
1942 merged_path,
1943 adm_access, FALSE,
1944 pool));
1945 if (((child_entry->kind == svn_node_dir)
1946 && (strcmp(merge_b->target, merged_path) == 0)
1947 && (depth == svn_depth_immediates))
1948 || ((child_entry->kind == svn_node_file)
1949 && (depth == svn_depth_files)))
1951 /* Set the explicit inheritable mergeinfo for,
1952 1. Merge target directory if depth is immediates.
1953 2. If merge is on a file and requested depth is 'files'.
1955 child_merge_range->inheritable = TRUE;
1956 rangelist_of_child = apr_array_make(pool, 1, sizeof(range));
1958 if (rangelist_of_child)
1960 APR_ARRAY_PUSH(rangelist_of_child, svn_merge_range_t *) =
1961 child_merge_range;
1963 apr_hash_set(*merges, (const char *)merged_path,
1964 APR_HASH_KEY_STRING, rangelist_of_child);
1969 return SVN_NO_ERROR;
1972 /* Calculate the new mergeinfo for the target tree based on the merge
1973 info for TARGET_WCPATH and MERGES (a mapping of WC paths to range
1974 lists), and record it in the WC (at, and possibly below,
1975 TARGET_WCPATH). */
1976 static svn_error_t *
1977 update_wc_mergeinfo(const char *target_wcpath, const svn_wc_entry_t *entry,
1978 const char *repos_rel_path, apr_hash_t *merges,
1979 svn_boolean_t is_rollback,
1980 svn_wc_adm_access_t *adm_access,
1981 svn_client_ctx_t *ctx, apr_pool_t *pool)
1983 apr_pool_t *subpool = svn_pool_create(pool);
1984 const char *rel_path;
1985 svn_mergeinfo_catalog_t mergeinfo;
1986 apr_hash_index_t *hi;
1988 /* Combine the mergeinfo for the revision range just merged into
1989 the WC with its on-disk mergeinfo. */
1990 for (hi = apr_hash_first(pool, merges); hi; hi = apr_hash_next(hi))
1992 const void *key;
1993 void *value;
1994 const char *path;
1995 apr_array_header_t *ranges, *rangelist;
1996 int len;
1997 svn_error_t *err;
1999 svn_pool_clear(subpool);
2001 apr_hash_this(hi, &key, NULL, &value);
2002 path = key;
2003 ranges = value;
2005 /* As some of the merges may've changed the WC's mergeinfo, get
2006 a fresh copy before using it to update the WC's mergeinfo. */
2007 err = svn_client__parse_mergeinfo(&mergeinfo, entry, path, FALSE,
2008 adm_access, ctx, subpool);
2009 /* If a directory PATH was skipped because it is missing or was
2010 obstructed by an unversioned item then there's nothing we can
2011 do with that, so skip it. */
2012 if (err)
2014 if (err->apr_err == SVN_ERR_WC_NOT_LOCKED)
2016 svn_error_clear(err);
2017 continue;
2019 else
2021 return err;
2025 /* If we are attempting to set empty revision range override mergeinfo
2026 on a path with no explicit mergeinfo, we first need the pristine
2027 mergeinfo that path inherits. */
2028 if (mergeinfo == NULL && ranges->nelts == 0)
2030 svn_boolean_t inherited;
2031 SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, &inherited, TRUE,
2032 svn_mergeinfo_nearest_ancestor,
2033 entry, path, NULL, NULL,
2034 adm_access, ctx, subpool));
2037 if (mergeinfo == NULL)
2038 mergeinfo = apr_hash_make(subpool);
2040 /* ASSUMPTION: "target_wcpath" is always both a parent and
2041 prefix of "path". */
2042 len = strlen(target_wcpath);
2043 if (len < strlen(path))
2045 const char *path_relative_to_target = len?(path + len + 1):(path);
2046 rel_path = apr_pstrcat(subpool, repos_rel_path, "/",
2047 path_relative_to_target, NULL);
2049 else
2050 rel_path = repos_rel_path;
2051 rangelist = apr_hash_get(mergeinfo, rel_path, APR_HASH_KEY_STRING);
2052 if (rangelist == NULL)
2053 rangelist = apr_array_make(subpool, 0, sizeof(svn_merge_range_t *));
2055 if (is_rollback)
2057 ranges = svn_rangelist_dup(ranges, subpool);
2058 SVN_ERR(svn_rangelist_reverse(ranges, subpool));
2059 SVN_ERR(svn_rangelist_remove(&rangelist, ranges, rangelist,
2060 FALSE,
2061 subpool));
2063 else
2065 SVN_ERR(svn_rangelist_merge(&rangelist, ranges,
2066 subpool));
2068 /* Update the mergeinfo by adjusting the path's rangelist. */
2069 apr_hash_set(mergeinfo, rel_path, APR_HASH_KEY_STRING, rangelist);
2071 if (is_rollback && apr_hash_count(mergeinfo) == 0)
2072 mergeinfo = NULL;
2074 svn_mergeinfo__remove_empty_rangelists(mergeinfo, pool);
2076 err = svn_client__record_wc_mergeinfo(path, mergeinfo,
2077 adm_access, subpool);
2079 if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
2081 /* PATH isn't just missing, it's not even versioned as far
2082 as this working copy knows. But it was included in
2083 MERGES, which means that the server knows about it.
2084 Likely we don't have access to the source due to authz
2085 restrictions. For now just clear the error and
2086 continue...
2088 ### TODO: Set non-inheritable mergeinfo on PATH's immediate
2089 ### parent and normal mergeinfo on PATH's siblings which we
2090 ### do have access to. */
2091 svn_error_clear(err);
2093 else
2094 SVN_ERR(err);
2097 svn_pool_destroy(subpool);
2098 return SVN_NO_ERROR;
2102 /* Create and return an error structure appropriate for the unmerged
2103 revisions range(s). */
2104 static APR_INLINE svn_error_t *
2105 make_merge_conflict_error(const char *target_wcpath,
2106 svn_merge_range_t *r,
2107 apr_pool_t *pool)
2109 return svn_error_createf
2110 (SVN_ERR_WC_FOUND_CONFLICT, NULL,
2111 _("One or more conflicts were produced while merging r%ld:%ld into\n"
2112 "'%s' --\n"
2113 "resolve all conflicts and rerun the merge to apply the remaining\n"
2114 "unmerged revisions"),
2115 r->start, r->end, svn_path_local_style(target_wcpath, pool));
2118 /* Helper for do_directory_merge().
2120 TARGET_WCPATH is a directory and CHILDREN_WITH_MERGEINFO is filled
2121 with paths (svn_client__merge_path_t *) arranged in depth first order,
2122 which have mergeinfo set on them or meet one of the other criteria
2123 defined in get_mergeinfo_paths(). Remove any paths absent from disk
2124 or scheduled for deletion from CHILDREN_WITH_MERGEINFO which are equal to
2125 or are descendants of TARGET_WCPATH by setting those children to NULL.
2126 Also remove the path from the NOTIFY_B->SKIPPED_PATHS hash. */
2127 static void
2128 remove_absent_children(const char *target_wcpath,
2129 apr_array_header_t *children_with_mergeinfo,
2130 notification_receiver_baton_t *notify_b)
2132 /* Before we try to override mergeinfo for skipped paths, make sure
2133 the path isn't absent due to authz restrictions, because there's
2134 nothing we can do about those. */
2135 int i;
2136 for (i = 0; i < children_with_mergeinfo->nelts; i++)
2138 svn_client__merge_path_t *child =
2139 APR_ARRAY_IDX(children_with_mergeinfo,
2140 i, svn_client__merge_path_t *);
2141 if (child
2142 && (child->absent || child->scheduled_for_deletion)
2143 && svn_path_is_ancestor(target_wcpath, child->path))
2145 if (notify_b->skipped_paths)
2146 apr_hash_set(notify_b->skipped_paths, child->path,
2147 APR_HASH_KEY_STRING, NULL);
2148 APR_ARRAY_IDX(children_with_mergeinfo, i,
2149 svn_client__merge_path_t *) = NULL;
2154 /* Sets up the diff editor report and drives it by properly negating
2155 subtree that could have a conflicting merge history.
2157 MERGE_B->ra_session1 reflects URL1; MERGE_B->ra_session2 reflects URL2.
2159 If MERGE_B->sources_ancestral is set, then URL1@REVISION1 must be a
2160 historical ancestor of URL2@REVISION2, or vice-versa (see
2161 `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements around
2162 the values of URL1, REVISION1, URL2, and REVISION2 in this case).
2164 static svn_error_t *
2165 drive_merge_report_editor(const char *target_wcpath,
2166 const char *url1,
2167 svn_revnum_t revision1,
2168 const char *url2,
2169 svn_revnum_t revision2,
2170 apr_array_header_t *children_with_mergeinfo,
2171 svn_boolean_t is_rollback,
2172 svn_depth_t depth,
2173 notification_receiver_baton_t *notify_b,
2174 svn_wc_adm_access_t *adm_access,
2175 const svn_wc_diff_callbacks2_t *callbacks,
2176 merge_cmd_baton_t *merge_b,
2177 apr_pool_t *pool)
2179 const svn_ra_reporter3_t *reporter;
2180 const svn_delta_editor_t *diff_editor;
2181 void *diff_edit_baton;
2182 void *report_baton;
2183 svn_revnum_t default_start;
2184 svn_boolean_t honor_mergeinfo;
2185 const char *old_sess2_url;
2187 mergeinfo_behavior(&honor_mergeinfo, NULL, merge_b);
2189 /* Calculate the default starting revision. */
2190 default_start = revision1;
2191 if (honor_mergeinfo)
2193 if (merge_b->target_has_dummy_merge_range)
2195 default_start = revision2;
2197 else if (children_with_mergeinfo && children_with_mergeinfo->nelts)
2199 svn_client__merge_path_t *child =
2200 APR_ARRAY_IDX(children_with_mergeinfo, 0,
2201 svn_client__merge_path_t *);
2202 if (child->remaining_ranges->nelts)
2204 svn_merge_range_t *range =
2205 APR_ARRAY_IDX(child->remaining_ranges, 0,
2206 svn_merge_range_t *);
2207 default_start = range->start;
2212 /* Temporarily point our second RA session to URL1, too. We use
2213 this to request individual file contents. */
2214 SVN_ERR(svn_client__ensure_ra_session_url(&old_sess2_url,
2215 merge_b->ra_session2,
2216 url1, pool));
2218 /* Get the diff editor and a reporter with which to, ultimately,
2219 drive it. */
2220 SVN_ERR(svn_client__get_diff_editor(target_wcpath, adm_access, callbacks,
2221 merge_b, depth, merge_b->dry_run,
2222 merge_b->ra_session2, default_start,
2223 notification_receiver, notify_b,
2224 merge_b->ctx->cancel_func,
2225 merge_b->ctx->cancel_baton,
2226 &diff_editor, &diff_edit_baton,
2227 pool));
2228 SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1,
2229 &reporter, &report_baton, revision2,
2230 "", depth, merge_b->ignore_ancestry,
2231 TRUE, /* text_deltas */
2232 url2, diff_editor, diff_edit_baton, pool));
2234 /* Drive the reporter. */
2235 SVN_ERR(reporter->set_path(report_baton, "", default_start, depth,
2236 FALSE, NULL, pool));
2237 if (honor_mergeinfo && children_with_mergeinfo)
2239 /* Describe children with mergeinfo overlapping this merge
2240 operation such that no repeated diff is retrieved for them from
2241 the repository. */
2242 apr_size_t target_wcpath_len = strlen(target_wcpath);
2243 int i;
2245 for (i = 1; i < children_with_mergeinfo->nelts; i++)
2247 svn_merge_range_t *range;
2248 const char *child_repos_path;
2249 svn_client__merge_path_t *child =
2250 APR_ARRAY_IDX(children_with_mergeinfo, i,
2251 svn_client__merge_path_t *);
2253 if (!child || child->absent || (child->remaining_ranges->nelts == 0))
2254 continue;
2256 range = APR_ARRAY_IDX(child->remaining_ranges, 0,
2257 svn_merge_range_t *);
2258 if (range->start == default_start)
2260 continue;
2262 else
2264 /* While we need to describe subtrees requiring different merge
2265 ranges than TARGET_WCPATH will have applied, we don't need to
2266 describe a subtree's subtree if that latter is having the
2267 same range applied as the former. */
2268 int j;
2269 svn_client__merge_path_t *parent = NULL;
2271 /* Does CHILD have a parent with mergeinfo other
2272 than TARGET_WCPATH? */
2273 for (j = i - 1; j > 0; j--)
2275 svn_client__merge_path_t *potential_parent =
2276 APR_ARRAY_IDX(children_with_mergeinfo, j,
2277 svn_client__merge_path_t *);
2278 if (svn_path_is_ancestor(potential_parent->path,
2279 child->path))
2281 parent = potential_parent;
2282 break;
2286 /* CHILD does have a parent with mergeinfo, if CHILD's first
2287 remaining range is the same as its parent there is no need
2288 to describe it separately. */
2289 if (parent && parent->remaining_ranges->nelts != 0)
2291 svn_merge_range_t *parent_range =
2292 APR_ARRAY_IDX(parent->remaining_ranges, 0,
2293 svn_merge_range_t *);
2294 svn_merge_range_t *child_range =
2295 APR_ARRAY_IDX(child->remaining_ranges, 0,
2296 svn_merge_range_t *);
2297 if (parent_range->start == child_range->start)
2298 continue;
2302 child_repos_path = child->path +
2303 (target_wcpath_len ? target_wcpath_len + 1 : 0);
2305 if ((is_rollback && (range->start < revision2))
2306 || (!is_rollback && (range->start > revision2)))
2308 SVN_ERR(reporter->set_path(report_baton, child_repos_path,
2309 revision2, depth, FALSE, NULL, pool));
2311 else
2313 SVN_ERR(reporter->set_path(report_baton, child_repos_path,
2314 range->start, depth, FALSE,
2315 NULL, pool));
2319 SVN_ERR(reporter->finish_report(report_baton, pool));
2321 /* Point the merge baton's second session back where it was. */
2322 if (old_sess2_url)
2323 SVN_ERR(svn_ra_reparent(merge_b->ra_session2, old_sess2_url, pool));
2325 /* Sleep to ensure timestamp integrity. */
2326 svn_sleep_for_timestamps();
2328 return SVN_NO_ERROR;
2331 /* Return the most inclusive range start revision across all the
2332 remaining ranges in CHILDREN_WITH_MERGEINFO. If there are no
2333 remaining ranges, return SVN_INVALID_REVNUM. Skip no-op ranges
2334 on the target (they are probably dummies). */
2335 static svn_revnum_t
2336 get_most_inclusive_start_rev(apr_array_header_t *children_with_mergeinfo,
2337 svn_boolean_t is_rollback)
2339 int i;
2340 svn_revnum_t start_rev = SVN_INVALID_REVNUM;
2342 for (i = 0; i < children_with_mergeinfo->nelts; i++)
2344 svn_client__merge_path_t *child =
2345 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
2346 svn_merge_range_t *range;
2348 if ((! child) || child->absent)
2349 continue;
2350 if (! child->remaining_ranges->nelts)
2351 continue;
2352 range = APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
2353 if ((i == 0) && (range->start == range->end))
2354 continue;
2355 if ((start_rev == SVN_INVALID_REVNUM)
2356 || (is_rollback && (range->start > start_rev))
2357 || ((! is_rollback) && (range->start < start_rev)))
2358 start_rev = range->start;
2360 return start_rev;
2363 /* Return the youngest qualifying end revision across the first of
2364 each child in CHILDREN_WITH_MERGEINFO's remaining ranges. If
2365 nothing qualifies, return SVN_INVALID_REVNUM. */
2366 static svn_revnum_t
2367 get_youngest_end_rev(apr_array_header_t *children_with_mergeinfo,
2368 svn_boolean_t is_rollback)
2370 int i;
2371 svn_revnum_t end_rev = SVN_INVALID_REVNUM;
2373 for (i = 0; i < children_with_mergeinfo->nelts; i++)
2375 svn_client__merge_path_t *child =
2376 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
2377 if (!child || child->absent)
2378 continue;
2379 if (child->remaining_ranges->nelts > 0)
2381 svn_merge_range_t *range = APR_ARRAY_IDX(child->remaining_ranges, 0,
2382 svn_merge_range_t *);
2383 if ((end_rev == SVN_INVALID_REVNUM)
2384 || (is_rollback && (range->end > end_rev))
2385 || ((! is_rollback) && (range->end < end_rev)))
2386 end_rev = range->end;
2389 return end_rev;
2392 /* If first item in each child of CHILDREN_WITH_MERGEINFO's
2393 remaining_ranges is inclusive of END_REV, Slice the first range in
2394 to two at END_REV. All the allocations are persistent and allocated
2395 from POOL. */
2396 static void
2397 slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
2398 svn_boolean_t is_rollback, svn_revnum_t end_rev,
2399 apr_pool_t *pool)
2401 int i;
2402 for (i = 0; i < children_with_mergeinfo->nelts; i++)
2404 svn_client__merge_path_t *child =
2405 APR_ARRAY_IDX(children_with_mergeinfo, i,
2406 svn_client__merge_path_t *);
2407 if (!child || child->absent)
2408 continue;
2409 if (child->remaining_ranges->nelts > 0)
2411 svn_merge_range_t *range = APR_ARRAY_IDX(child->remaining_ranges, 0,
2412 svn_merge_range_t *);
2413 if ((is_rollback && (range->start > end_rev)
2414 && (range->end < end_rev))
2415 || (!is_rollback && (range->start < end_rev)
2416 && (range->end > end_rev)))
2418 int j;
2419 svn_merge_range_t *split_range1, *split_range2;
2420 apr_array_header_t *orig_remaining_ranges =
2421 child->remaining_ranges;
2422 split_range1 = svn_merge_range_dup(range, pool);
2423 split_range2 = svn_merge_range_dup(range, pool);
2424 split_range1->end = end_rev;
2425 split_range2->start = end_rev;
2426 child->remaining_ranges =
2427 apr_array_make(pool, (child->remaining_ranges->nelts + 1),
2428 sizeof(svn_merge_range_t *));
2429 APR_ARRAY_PUSH(child->remaining_ranges,
2430 svn_merge_range_t *) = split_range1;
2431 APR_ARRAY_PUSH(child->remaining_ranges,
2432 svn_merge_range_t *) = split_range2;
2433 for (j = 1; j < orig_remaining_ranges->nelts; j++)
2435 svn_merge_range_t *orig_range =
2436 APR_ARRAY_IDX(orig_remaining_ranges, j,
2437 svn_merge_range_t *);
2438 APR_ARRAY_PUSH(child->remaining_ranges,
2439 svn_merge_range_t *) = orig_range;
2446 /* For each child of CHILDREN_WITH_MERGEINFO create a new remaining_ranges
2447 by removing the first item from the original range list and overwrite the
2448 original remaining_ranges with this new list.
2449 All the allocations are persistent from a POOL.
2450 TODO, we should have remaining_ranges in reverse order to avoid recreating
2451 the remaining_ranges every time instead of one 'pop' operation. */
2452 static void
2453 remove_first_range_from_remaining_ranges(
2454 apr_array_header_t *children_with_mergeinfo,
2455 apr_pool_t *pool)
2457 int i, j;
2458 for (i = 0; i < children_with_mergeinfo->nelts; i++)
2460 svn_client__merge_path_t *child =
2461 APR_ARRAY_IDX(children_with_mergeinfo, i,
2462 svn_client__merge_path_t *);
2463 if (!child || child->absent)
2464 continue;
2465 if (child->remaining_ranges->nelts > 0)
2467 apr_array_header_t *orig_remaining_ranges = child->remaining_ranges;
2468 child->remaining_ranges =
2469 apr_array_make(pool, (child->remaining_ranges->nelts - 1),
2470 sizeof(svn_merge_range_t *));
2471 for (j = 1; j < orig_remaining_ranges->nelts; j++)
2473 svn_merge_range_t *range = APR_ARRAY_IDX(orig_remaining_ranges,
2475 svn_merge_range_t *);
2476 APR_ARRAY_PUSH(child->remaining_ranges, svn_merge_range_t *)
2477 = range;
2484 /* Blindly record the range specified by the user (rather than refining it
2485 as we do for actual merges) for the merge source URL. */
2486 static svn_error_t *
2487 record_mergeinfo_for_record_only_merge(const char *url,
2488 svn_merge_range_t *range,
2489 const svn_wc_entry_t *entry,
2490 svn_wc_adm_access_t *adm_access,
2491 merge_cmd_baton_t *merge_b,
2492 apr_pool_t *pool)
2494 apr_array_header_t *rangelist;
2495 const char *rel_path;
2496 svn_mergeinfo_t target_mergeinfo;
2497 svn_boolean_t indirect;
2498 apr_hash_t *merges = apr_hash_make(pool);
2499 const char *old_url = NULL;
2500 svn_boolean_t is_rollback = (range->start > range->end);
2502 /* Temporarily reparent ra_session to WC target URL. */
2503 SVN_ERR(svn_client__ensure_ra_session_url(&old_url, merge_b->ra_session1,
2504 entry->url, pool));
2505 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(&target_mergeinfo, entry,
2506 &indirect, FALSE,
2507 svn_mergeinfo_inherited,
2508 merge_b->ra_session1,
2509 merge_b->target,
2510 adm_access, merge_b->ctx,
2511 pool));
2512 if (old_url)
2513 SVN_ERR(svn_ra_reparent(merge_b->ra_session1, url, pool));
2515 SVN_ERR(svn_client__path_relative_to_root(&rel_path, url, NULL, TRUE,
2516 merge_b->ra_session1,
2517 adm_access, pool));
2518 rangelist = apr_array_make(pool, 1, sizeof(range));
2519 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = range;
2520 apr_hash_set(merges, merge_b->target, APR_HASH_KEY_STRING, rangelist);
2522 /* If merge target has indirect mergeinfo set it. */
2523 if (indirect)
2524 SVN_ERR(svn_client__record_wc_mergeinfo(merge_b->target, target_mergeinfo,
2525 adm_access, pool));
2527 return update_wc_mergeinfo(merge_b->target, entry, rel_path, merges,
2528 is_rollback, adm_access, merge_b->ctx, pool);
2531 /* Marks 'inheritable' RANGE to TARGET_WCPATH by wiping off the
2532 corresponding 'non-inheritable' RANGE from TARGET_MERGEINFO for the
2533 merge source REL_PATH. It does such marking only for same URLs
2534 from same Repository, not a dry run, target having existing
2535 mergeinfo(TARGET_MERGEINFO) and target being part of
2536 CHILDREN_WITH_MERGEINFO. */
2537 static svn_error_t *
2538 mark_mergeinfo_as_inheritable_for_a_range(
2539 svn_mergeinfo_t target_mergeinfo,
2540 svn_boolean_t same_urls,
2541 svn_merge_range_t *range,
2542 const char *rel_path,
2543 const char *target_wcpath,
2544 svn_wc_adm_access_t *adm_access,
2545 merge_cmd_baton_t *merge_b,
2546 apr_array_header_t *children_with_mergeinfo,
2547 int target_index, apr_pool_t *pool)
2549 /* Check if we need to make non-inheritable ranges inheritable. */
2550 if (target_mergeinfo && same_urls
2551 && !merge_b->dry_run
2552 && merge_b->same_repos
2553 && target_index >= 0)
2555 svn_client__merge_path_t *merge_path =
2556 APR_ARRAY_IDX(children_with_mergeinfo,
2557 target_index, svn_client__merge_path_t *);
2559 /* If a path has no missing children, has non-inheritable ranges,
2560 *and* those non-inheritable ranges intersect with the merge being
2561 performed (i.e. this is a repeat merge where a previously missing
2562 child is now present) then those non-inheritable ranges are made
2563 inheritable. */
2564 if (merge_path
2565 && merge_path->has_noninheritable && !merge_path->missing_child)
2567 svn_boolean_t is_equal;
2568 apr_hash_t *merges;
2569 apr_hash_t *inheritable_merges = apr_hash_make(pool);
2570 apr_array_header_t *inheritable_ranges =
2571 apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
2573 APR_ARRAY_PUSH(inheritable_ranges, svn_merge_range_t *) = range;
2574 apr_hash_set(inheritable_merges, rel_path, APR_HASH_KEY_STRING,
2575 inheritable_ranges);
2577 /* Try to remove any non-inheritable ranges bound by the merge
2578 being performed. */
2579 SVN_ERR(svn_mergeinfo_inheritable(&merges, target_mergeinfo,
2580 rel_path, range->start,
2581 range->end, pool));
2582 /* If any non-inheritable ranges were removed put them back as
2583 inheritable ranges. */
2584 SVN_ERR(svn_mergeinfo__equals(&is_equal, merges, target_mergeinfo,
2585 FALSE, pool));
2586 if (!is_equal)
2588 SVN_ERR(svn_mergeinfo_merge(merges, inheritable_merges, pool));
2589 SVN_ERR(svn_client__record_wc_mergeinfo(target_wcpath, merges,
2590 adm_access, pool));
2594 return SVN_NO_ERROR;
2597 /* For shallow merges record the explicit *indirect* mergeinfo on the
2599 1. merged files *merged* with a depth 'files'.
2600 2. merged target directory *merged* with a depth 'immediates'.
2602 All subtrees which are going to get a 'inheritable merge range'
2603 because of this 'shallow' merge should have the explicit mergeinfo
2604 recorded on them. */
2605 static svn_error_t *
2606 record_mergeinfo_on_merged_children(svn_depth_t depth,
2607 svn_wc_adm_access_t *adm_access,
2608 notification_receiver_baton_t *notify_b,
2609 merge_cmd_baton_t *merge_b,
2610 apr_pool_t *pool)
2612 if ((depth != svn_depth_infinity) && notify_b->merged_paths)
2614 svn_boolean_t indirect_child_mergeinfo = FALSE;
2615 apr_hash_index_t *hi;
2616 svn_mergeinfo_t child_target_mergeinfo;
2617 const void *merged_path;
2619 for (hi = apr_hash_first(NULL, notify_b->merged_paths); hi;
2620 hi = apr_hash_next(hi))
2622 const svn_wc_entry_t *child_entry;
2623 apr_hash_this(hi, &merged_path, NULL, NULL);
2624 SVN_ERR(svn_wc__entry_versioned(&child_entry, merged_path,
2625 adm_access, FALSE, pool));
2626 if (((child_entry->kind == svn_node_dir)
2627 && (strcmp(merge_b->target, merged_path) == 0)
2628 && (depth == svn_depth_immediates))
2629 || ((child_entry->kind == svn_node_file)
2630 && (depth == svn_depth_files)))
2632 /* Set the explicit inheritable mergeinfo for,
2633 1. Merge target directory if depth is 'immediates'.
2634 2. If merge is on a file and requested depth is 'files'.
2636 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo
2637 (&child_target_mergeinfo, child_entry,
2638 &indirect_child_mergeinfo,
2639 FALSE, svn_mergeinfo_inherited,
2640 merge_b->ra_session1, merged_path,
2641 adm_access, merge_b->ctx, pool));
2642 if (indirect_child_mergeinfo)
2643 SVN_ERR(svn_client__record_wc_mergeinfo(merged_path,
2644 child_target_mergeinfo,
2645 adm_access, pool));
2649 return SVN_NO_ERROR;
2653 /* Get REVISION of the file at URL. SOURCE is a path that refers to that
2654 file's entry in the working copy, or NULL if we don't have one. Return in
2655 *FILENAME the name of a file containing the file contents, in *PROPS a hash
2656 containing the properties and in *REV the revision. All allocation occurs
2657 in POOL. */
2658 static svn_error_t *
2659 single_file_merge_get_file(const char **filename,
2660 svn_ra_session_t *ra_session,
2661 apr_hash_t **props,
2662 svn_revnum_t rev,
2663 const char *wc_target,
2664 apr_pool_t *pool)
2666 apr_file_t *fp;
2667 svn_stream_t *stream;
2669 /* ### Create this temporary file under .svn/tmp/ instead of next to
2670 ### the working file.*/
2671 SVN_ERR(svn_io_open_unique_file2(&fp, filename,
2672 wc_target, ".tmp",
2673 svn_io_file_del_none, pool));
2674 stream = svn_stream_from_aprfile2(fp, FALSE, pool);
2675 SVN_ERR(svn_ra_get_file(ra_session, "", rev,
2676 stream, NULL, props, pool));
2677 SVN_ERR(svn_stream_close(stream));
2679 return SVN_NO_ERROR;
2683 /* Send a notification specific to a single-file merge if the states
2684 indicate there's something worth reporting.
2686 If *HEADER_SENT is not set and HEADER_NOTIFICATION is not NULL, then
2687 send the header notification before sending the state notification,
2688 and set *HEADER_SENT to TRUE. */
2689 static APR_INLINE void
2690 single_file_merge_notify(void *notify_baton,
2691 const char *target_wcpath,
2692 svn_wc_notify_action_t action,
2693 svn_wc_notify_state_t text_state,
2694 svn_wc_notify_state_t prop_state,
2695 svn_wc_notify_t *header_notification,
2696 svn_boolean_t *header_sent,
2697 apr_pool_t *pool)
2699 svn_wc_notify_t *notify = svn_wc_create_notify(target_wcpath, action, pool);
2700 notify->kind = svn_node_file;
2701 notify->content_state = text_state;
2702 notify->prop_state = prop_state;
2703 if (notify->content_state == svn_wc_notify_state_missing)
2704 notify->action = svn_wc_notify_skip;
2706 if (IS_OPERATIVE_NOTIFICATION(notify)
2707 && header_notification
2708 && (! *header_sent))
2710 notification_receiver(notify_baton, header_notification, pool);
2711 *header_sent = TRUE;
2713 notification_receiver(notify_baton, notify, pool);
2717 /* A baton for get_mergeinfo_walk_cb. */
2718 struct get_mergeinfo_walk_baton
2720 /* Access for the tree being walked. */
2721 svn_wc_adm_access_t *base_access;
2722 /* Array of paths that have explicit mergeinfo and/or are switched. */
2723 apr_array_header_t *children_with_mergeinfo;
2724 /* Merge source canonical path. */
2725 const char* merge_src_canon_path;
2727 /* Information on the merge cascaded from do_directory_merge() */
2728 const char* merge_target_path;
2729 const char *source_root_url;
2730 const char* url1;
2731 const char* url2;
2732 svn_revnum_t revision1;
2733 svn_revnum_t revision2;
2735 /* Cascaded from MERGE_CMD_BATON_T members of the same names. */
2736 svn_boolean_t first_range;
2737 svn_mergeinfo_t working_mergeinfo;
2738 apr_pool_t *long_pool;
2740 /* merge depth requested. */
2741 svn_depth_t depth;
2743 /* RA session and client context cascaded from do_directory_merge() */
2744 svn_ra_session_t *ra_session;
2745 svn_client_ctx_t *ctx;
2749 /* svn_wc_entry_callbacks2_t found_entry() callback for get_mergeinfo_paths.
2751 Given PATH, its corresponding ENTRY, and WB, where WB is the WALK_BATON
2752 of type "struct get_mergeinfo_walk_baton *": If PATH is switched,
2753 has explicit working svn:mergeinfo from a corresponding merge source, is
2754 missing a child due to a sparse checkout, is absent from disk, or is
2755 scheduled for deletion, then create a svn_client__merge_path_t *
2756 representing *PATH, allocated in WB->CHILDREN_WITH_MERGEINFO->POOL, and
2757 push it onto the WB->CHILDREN_WITH_MERGEINFO array. */
2758 static svn_error_t *
2759 get_mergeinfo_walk_cb(const char *path,
2760 const svn_wc_entry_t *entry,
2761 void *walk_baton,
2762 apr_pool_t *pool)
2764 struct get_mergeinfo_walk_baton *wb = walk_baton;
2765 const svn_string_t *propval;
2766 svn_mergeinfo_t mergehash;
2767 svn_boolean_t switched = FALSE;
2768 svn_boolean_t has_mergeinfo_from_merge_src = FALSE;
2769 svn_boolean_t path_is_merge_target =
2770 !svn_path_compare_paths(path, wb->merge_target_path);
2771 const char *parent_path = svn_path_dirname(path, pool);
2773 /* We're going to receive dirents twice; we want to ignore the
2774 first one (where it's a child of a parent dir), and only use
2775 the second one (where we're looking at THIS_DIR). The exception
2776 is absent dirs, these only come through once, so continue. */
2777 if ((entry->kind == svn_node_dir)
2778 && (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) != 0)
2779 && !entry->absent)
2780 return SVN_NO_ERROR;
2782 /* Ignore the entry if it does not exist at the time of interest. */
2783 if (entry->deleted)
2784 return SVN_NO_ERROR;
2786 if (entry->absent || entry->schedule == svn_wc_schedule_delete)
2788 propval = NULL;
2789 switched = FALSE;
2791 else
2793 SVN_ERR(svn_wc_prop_get(&propval, SVN_PROP_MERGEINFO, path,
2794 wb->base_access, pool));
2795 /* We always include the merge target regardless of its mergeinfo.
2796 So we don't need to check that PATH's mergeinfo corresponds to
2797 the merge source. */
2798 if (propval && !path_is_merge_target)
2800 svn_stringbuf_t *merge_src_child_path =
2801 svn_stringbuf_create(wb->merge_src_canon_path, pool);
2803 /* When the merge target is '' or '.' WB->MERGE_TARGET_PATH is
2804 an empty string and PATH will always be relative. In this case
2805 we can safely combine WB->MERGE_SRC_CANON_PATH and PATH with
2806 svn_path_add_compent() which will supply the missing '/' separator.
2808 Otherwise WB->MERGE_TARGET_PATH is relative or absolute and
2809 we remove the common root component between WB->MERGE_TARGET_PATH
2810 and PATH from PATH before combining it with
2811 WB->MERGE_SRC_CANON_PATH. The +1 is required because if we are
2812 here that means WB->MERGE_TARGET_PATH is a proper ancestor of
2813 PATH and we must skip the path separator -- svn_path_add_compent()
2814 will add missing separators, but won't remove existing ones -- to
2815 avoid a merge_src_child_path with "//" in it. */
2816 if (strlen(wb->merge_target_path))
2817 svn_path_add_component(merge_src_child_path,
2818 path + strlen(wb->merge_target_path) + 1);
2819 else
2820 svn_path_add_component(merge_src_child_path,
2821 path);
2822 SVN_ERR(svn_mergeinfo_parse(&mergehash, propval->data, pool));
2823 if (apr_hash_get(mergehash, merge_src_child_path->data,
2824 APR_HASH_KEY_STRING))
2826 /* The easy way: PATH already has mergeinfo
2827 from this source... */
2828 has_mergeinfo_from_merge_src = TRUE;
2830 else
2832 /* ...the slightly harder way: See if PATH exists in the
2833 merge source at the revisions being merged. If it doesn't
2834 exist there is no way this subtree can be affected by the
2835 merge so we can safely leave it, and its mergeinfo, alone. */
2836 svn_error_t *err;
2837 const char *original_ra_url = NULL;
2838 const char *mergeinfo_url =
2839 svn_path_join(wb->source_root_url,
2840 /* Skip leading '/' or join won't work. */
2841 ++(merge_src_child_path->data),
2842 pool);
2843 svn_opt_revision_t *start_revision, *end_revision;
2844 const char *start_url, *end_url;
2845 svn_opt_revision_t peg_rev, rev1_opt, rev2_opt;
2847 peg_rev.value.number = wb->revision1 < wb->revision2
2848 ? wb->revision2 : wb->revision1;
2849 peg_rev.kind = svn_opt_revision_number;
2851 rev1_opt.kind = svn_opt_revision_number;
2852 rev1_opt.value.number = wb->revision1;
2854 rev2_opt.kind = svn_opt_revision_number;
2855 rev2_opt.value.number = wb->revision2;
2857 /* Instead of passing NULL to svn_client__repos_locations() and
2858 causing another session to open, reparent WB->RA_SESSION
2859 and use that. */
2860 SVN_ERR(svn_client__ensure_ra_session_url(&original_ra_url,
2861 wb->ra_session,
2862 mergeinfo_url, pool));
2864 /* Does PATH exist in the merge source? */
2865 err = svn_client__repos_locations(&start_url, &start_revision,
2866 &end_url, &end_revision,
2867 wb->ra_session, mergeinfo_url,
2868 &peg_rev, &rev1_opt, &rev2_opt,
2869 wb->ctx, pool);
2870 if (err)
2872 /* We might see any of these errors depending on the RA
2873 access method, but they all mean that PATH doesn't exist
2874 in the merge source.
2876 ### TODO: Make svn_client__repos_locations() more
2877 ### consistent in the error it returns(?)
2879 if (err->apr_err == SVN_ERR_FS_NOT_FOUND
2880 || err->apr_err == SVN_ERR_RA_DAV_PATH_NOT_FOUND
2881 || err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
2882 svn_error_clear(err);
2883 else
2884 return err;
2887 /* Reparent the session to its original URL if necessary. */
2888 if (original_ra_url)
2890 SVN_ERR(svn_ra_reparent(wb->ra_session,
2891 original_ra_url, pool));
2894 if (!err) /* PATH does exist in the merge source*/
2896 if (propval->len > 0)
2898 has_mergeinfo_from_merge_src = TRUE;
2900 else /* Handle the special case of empty mergeinfo */
2902 /* WC->WC copies (and moves) can't contact the server
2903 and so leave empty mergefino on the copy target to
2904 prevent it from erroneously inheriting the incorrect
2905 mergeinfo from the repos (if any exists) during a
2906 merge. Reverse merges may also leave empty mergeinfo
2907 on a path so this state is not exclusive to WC->WC
2908 copies.
2910 Because the WC->WC copy behavior is a safety net,
2911 the empty mergeinfo on a path *may* not actually
2912 override anything. In the case where this is true,
2913 we don't need to treat PATH as a subtree with
2914 intersecting mergeinfo, we can simply merge into
2915 PATH's parent (which may be the merge target
2916 itself)...
2918 ...So, if we find empty mergeinfo on PATH see if
2919 PATH has any ancestor with mergeinfo. */
2920 svn_mergeinfo_t overidden_mergeinfo;
2921 svn_boolean_t indirect;
2922 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(
2923 &overidden_mergeinfo, entry,
2924 &indirect, FALSE, svn_mergeinfo_nearest_ancestor,
2925 wb->ra_session, path, wb->base_access, wb->ctx,
2926 pool));
2927 if (indirect)
2929 /* Ok, we found empty mergeinfo which overrides some
2930 parent with mergeinfo. Time for one final
2931 optimization: If the mergeinfo overridden is
2932 *also* empty, then we can perform some preemptive
2933 elision right now and remove the empty mergeinfo
2934 from PATH. Even if the overriden parent is in
2935 the repository it's ok, since the merge target
2936 *always* gets mergeinfo set (though it may
2937 elide).*/
2938 svn_boolean_t equal_mergeinfo;
2939 SVN_ERR(svn_mergeinfo__equals(&equal_mergeinfo,
2940 mergehash,
2941 overidden_mergeinfo,
2942 FALSE, pool));
2943 if (equal_mergeinfo)
2944 SVN_ERR(svn_client__record_wc_mergeinfo(
2945 path, NULL, wb->base_access, pool));
2946 else
2947 /* The empty mergeinfo on PATH really means
2948 something. Merge this subtree separately. */
2949 has_mergeinfo_from_merge_src = TRUE;
2952 } /* (!err) */
2953 start_url = NULL;
2954 } /* the slightly harder way */
2956 /* Regardless of whether PATH has explicit mergeinfo or not, we must
2957 determine if PATH is switched. This is so get_mergeinfo_paths()
2958 can later tweak PATH's parent to reflect a missing child (implying it
2959 needs non-inheritable mergeinfo ranges) and PATH's siblings so they
2960 get their own complete set of mergeinfo. */
2961 SVN_ERR(svn_wc__path_switched(path, &switched, entry, pool));
2964 /* Store PATHs with explict mergeinfo, which are switched, are missing
2965 children due to a sparse checkout, are scheduled for deletion are absent
2966 from the WC, and/or are first level sub directories relative to merge
2967 target if depth is immediates. */
2968 if (path_is_merge_target
2969 || has_mergeinfo_from_merge_src
2970 || entry->schedule == svn_wc_schedule_delete
2971 || switched
2972 || entry->depth == svn_depth_empty
2973 || entry->depth == svn_depth_files
2974 || entry->absent
2975 || ((wb->depth == svn_depth_immediates) &&
2976 (entry->kind == svn_node_dir) &&
2977 (strcmp(parent_path, path) != 0) &&
2978 (strcmp(parent_path, wb->merge_target_path) == 0)))
2980 svn_client__merge_path_t *child =
2981 apr_pcalloc(wb->children_with_mergeinfo->pool, sizeof(*child));
2982 child->path = apr_pstrdup(wb->children_with_mergeinfo->pool, path);
2983 child->missing_child = (entry->depth == svn_depth_empty
2984 || entry->depth == svn_depth_files
2985 || ((wb->depth == svn_depth_immediates) &&
2986 (entry->kind == svn_node_dir) &&
2987 (strcmp(parent_path,
2988 wb->merge_target_path) == 0)))
2989 ? TRUE : FALSE;
2990 child->switched = switched;
2991 child->absent = entry->absent;
2992 child->scheduled_for_deletion =
2993 entry->schedule == svn_wc_schedule_delete ? TRUE : FALSE;
2994 if (propval)
2996 if (wb->first_range)
2998 working_mergeinfo_t *working_mergeinfo =
2999 apr_pcalloc(wb->long_pool, sizeof(*working_mergeinfo));
3000 working_mergeinfo->working_mergeinfo_propval =
3001 svn_string_create(propval->data, wb->long_pool);
3002 apr_hash_set(wb->working_mergeinfo, child->path,
3003 APR_HASH_KEY_STRING, working_mergeinfo);
3005 if (strstr(propval->data, SVN_MERGEINFO_NONINHERITABLE_STR))
3006 child->has_noninheritable = TRUE;
3009 /* A little trickery: If PATH doesn't have any mergeinfo or has
3010 only inheritable mergeinfo, we still describe it as having
3011 non-inheritable mergeinfo if it is missing a child. Why? Because
3012 the mergeinfo we'll add to PATH as a result of the merge will need
3013 to be non-inheritable (since PATH is missing children) and doing
3014 this now allows get_mergeinfo_paths() to properly account for PATH's
3015 other children. */
3016 if (!child->has_noninheritable
3017 && (entry->depth == svn_depth_empty
3018 || entry->depth == svn_depth_files))
3019 child->has_noninheritable = TRUE;
3021 APR_ARRAY_PUSH(wb->children_with_mergeinfo,
3022 svn_client__merge_path_t *) = child;
3025 return SVN_NO_ERROR;
3028 /* svn_wc_entry_callbacks2_t handle_error() callback for
3029 get_mergeinfo_paths().
3031 Squelch ERR by returning SVN_NO_ERROR if ERR is caused by a missing
3032 path (i.e. SVN_ERR_WC_PATH_NOT_FOUND) or an unversioned path
3033 (i.e. SVN_ERR_WC_NOT_LOCKED). */
3034 static svn_error_t *
3035 get_mergeinfo_error_handler(const char *path,
3036 svn_error_t *err,
3037 void *walk_baton,
3038 apr_pool_t *pool)
3040 svn_error_t *root_err = svn_error_root_cause(err);
3041 if (root_err == SVN_NO_ERROR)
3042 return err;
3044 switch (root_err->apr_err)
3046 case SVN_ERR_WC_PATH_NOT_FOUND:
3047 case SVN_ERR_WC_NOT_LOCKED:
3048 svn_error_clear(err);
3049 return SVN_NO_ERROR;
3051 default:
3052 return err;
3056 /* Helper for get_mergeinfo_paths()
3058 CHILDREN_WITH_MERGEINFO is a depth first sorted array filled with
3059 svn_client__merge_path_t *. Starting at the element in
3060 CHILDREN_WITH_MERGEINFO located at START_INDEX look for that
3061 element's child/parent (as indicated by LOOKING_FOR_CHILD) named
3062 PATH. If the child/parent is found, set *CHILD_OR_PARENT to that
3063 element and return the index at which if was found. If the
3064 child/parent is not found set *CHILD_OR_PARENT to NULL and return
3065 the index at which it should be inserted. */
3066 static int
3067 find_child_or_parent(apr_array_header_t *children_with_mergeinfo,
3068 svn_client__merge_path_t **child_or_parent,
3069 const char *path,
3070 svn_boolean_t looking_for_child,
3071 int start_index,
3072 apr_pool_t *pool)
3074 int j = 0;
3075 *child_or_parent = NULL;
3077 /* If possible, search forwards in the depth first sorted array
3078 to find a child PATH or backwards to find a parent PATH. */
3079 if (start_index >= 0 && start_index < children_with_mergeinfo->nelts)
3081 for (j = looking_for_child ? start_index + 1 : start_index;
3082 looking_for_child ? j < children_with_mergeinfo->nelts : j >= 0;
3083 j = looking_for_child ? j + 1 : j - 1)
3085 /* If this potential child is neither the child we are looking for
3086 or another one of PARENT's children then CHILD_PATH doesn't
3087 exist in CHILDREN_WITH_MERGEINFO. */
3088 svn_client__merge_path_t *potential_child_or_parent =
3089 APR_ARRAY_IDX(children_with_mergeinfo, j,
3090 svn_client__merge_path_t *);
3091 int cmp = svn_path_compare_paths(path,
3092 potential_child_or_parent->path);
3093 if (cmp == 0)
3095 /* Found child or parent. */
3096 *child_or_parent = potential_child_or_parent;
3097 break;
3099 else if ((looking_for_child && cmp < 0)
3100 || (!looking_for_child && cmp > 0))
3102 /* PATH doesn't exist, but found where it should be inserted. */
3103 if (!looking_for_child)
3104 j++;
3105 break;
3107 else if (!looking_for_child && j == 0)
3109 /* Looking for a parent but are at start of the array so we know
3110 where to insert the parent. */
3111 break;
3113 /* else we are looking for a child but found one of its
3114 siblings...keep looking. */
3117 return j;
3120 /* Helper for get_mergeinfo_paths()
3122 CHILDREN_WITH_MERGEINFO is a depth first sorted array filled with
3123 svn_client__merge_path_t *. Insert INSERT_ELEMENT into the
3124 CHILDREN_WITH_MERGEINFO array at index INSERT_INDEX. */
3125 static void
3126 insert_child_to_merge(apr_array_header_t *children_with_mergeinfo,
3127 svn_client__merge_path_t *insert_element,
3128 int insert_index)
3130 if (insert_index == children_with_mergeinfo->nelts)
3132 APR_ARRAY_PUSH(children_with_mergeinfo,
3133 svn_client__merge_path_t *) = insert_element;
3135 else
3137 /* Copy the last element of CHILDREN_WITH_MERGEINFO and add it to the
3138 end of the array. */
3139 int j;
3140 svn_client__merge_path_t *curr =
3141 APR_ARRAY_IDX(children_with_mergeinfo,
3142 children_with_mergeinfo->nelts - 1,
3143 svn_client__merge_path_t *);
3144 svn_client__merge_path_t *curr_copy =
3145 apr_palloc(children_with_mergeinfo->pool, sizeof(*curr_copy));
3147 *curr_copy = *curr;
3148 APR_ARRAY_PUSH(children_with_mergeinfo,
3149 svn_client__merge_path_t *) = curr_copy;
3151 /* Move all elements from INSERT_INDEX to the end of the array forward
3152 one spot then insert the new element. */
3153 for (j = children_with_mergeinfo->nelts - 2; j >= insert_index; j--)
3155 svn_client__merge_path_t *prev;
3156 curr = APR_ARRAY_IDX(children_with_mergeinfo, j,
3157 svn_client__merge_path_t *);
3158 if (j == insert_index)
3159 *curr = *insert_element;
3160 else
3162 prev = APR_ARRAY_IDX(children_with_mergeinfo, j - 1,
3163 svn_client__merge_path_t *);
3164 *curr = *prev;
3170 /* Helper for get_mergeinfo_paths()'s qsort() call. */
3171 static int
3172 compare_merge_path_t_as_paths(const void *a,
3173 const void *b)
3175 svn_client__merge_path_t *child1 = *((svn_client__merge_path_t * const *) a);
3176 svn_client__merge_path_t *child2 = *((svn_client__merge_path_t * const *) b);
3178 return svn_path_compare_paths(child1->path, child2->path);
3181 /* Helper for get_mergeinfo_paths(). If CHILD->PATH is switched,
3182 absent, or scheduled for deletion make sure its parent is marked
3183 as missing a child. Start looking up for parent from *CURR_INDEX
3184 in CHILDREN_WITH_MERGEINFO. Create the parent and insert it into
3185 CHILDREN_WITH_MERGEINFO if necessary (and increment *CURR_INDEX
3186 so that caller don't process the inserted element). Also ensure
3187 that CHILD->PATH's siblings which are not already present in
3188 CHILDREN_WITH_MERGEINFO are also added to the array. Use POOL for
3189 all temporary allocations. */
3190 static svn_error_t *
3191 insert_parent_and_sibs_of_sw_absent_del_entry(
3192 apr_array_header_t *children_with_mergeinfo,
3193 merge_cmd_baton_t *merge_cmd_baton,
3194 int *curr_index,
3195 svn_client__merge_path_t *child,
3196 svn_wc_adm_access_t *adm_access,
3197 apr_pool_t *pool)
3199 svn_client__merge_path_t *parent;
3200 const char *parent_path = svn_path_dirname(child->path, pool);
3201 apr_hash_t *entries;
3202 apr_hash_index_t *hi;
3203 svn_wc_adm_access_t *parent_access;
3204 int insert_index, parent_index;
3206 if (!(child->absent
3207 || child->scheduled_for_deletion
3208 || (child->switched
3209 && strcmp(merge_cmd_baton->target, child->path) != 0)))
3210 return SVN_NO_ERROR;
3212 parent_index = find_child_or_parent(children_with_mergeinfo, &parent,
3213 parent_path, FALSE, *curr_index, pool);
3214 if (parent)
3216 parent->missing_child = TRUE;
3218 else
3220 /* Create a new element to insert into CHILDREN_WITH_MERGEINFO. */
3221 parent = apr_pcalloc(children_with_mergeinfo->pool, sizeof(*parent));
3222 parent->path = apr_pstrdup(children_with_mergeinfo->pool, parent_path);
3223 parent->missing_child = TRUE;
3224 /* Insert PARENT into CHILDREN_WITH_MERGEINFO. */
3225 insert_child_to_merge(children_with_mergeinfo, parent, parent_index);
3226 /* Increment for loop index so we don't process the inserted element. */
3227 (*curr_index)++;
3228 } /*(parent == NULL) */
3230 /* Add all of PARENT's non-missing children that are not already present.*/
3231 SVN_ERR(svn_wc_adm_probe_try3(&parent_access, adm_access, parent->path,
3232 TRUE, -1, merge_cmd_baton->ctx->cancel_func,
3233 merge_cmd_baton->ctx->cancel_baton, pool));
3234 SVN_ERR(svn_wc_entries_read(&entries, parent_access, FALSE, pool));
3235 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
3237 const void *key;
3238 svn_client__merge_path_t *sibling_of_missing;
3239 const char *child_path;
3241 apr_hash_this(hi, &key, NULL, NULL);
3243 if (strcmp(key, SVN_WC_ENTRY_THIS_DIR) == 0)
3244 continue;
3246 /* Does this child already exist in CHILDREN_WITH_MERGEINFO? */
3247 child_path = svn_path_join(parent->path, key, pool);
3248 insert_index = find_child_or_parent(children_with_mergeinfo,
3249 &sibling_of_missing, child_path,
3250 TRUE, parent_index, pool);
3251 /* Create the missing child and insert it into CHILDREN_WITH_MERGEINFO.*/
3252 if (!sibling_of_missing)
3254 sibling_of_missing = apr_pcalloc(children_with_mergeinfo->pool,
3255 sizeof(*sibling_of_missing));
3256 sibling_of_missing->path = apr_pstrdup(children_with_mergeinfo->pool,
3257 child_path);
3258 insert_child_to_merge(children_with_mergeinfo, sibling_of_missing,
3259 insert_index);
3262 return SVN_NO_ERROR;
3265 /* Helper for do_directory_merge()
3267 Perform a depth first walk of the working copy tree rooted at
3268 MERGE_CMD_BATON->TARGET (with the corresponding ENTRY). Create an
3269 svn_client__merge_path_t * for any path which meets one or more of the
3270 following criteria:
3272 1) Path has working svn:mergeinfo from corresponding merge source.
3273 2) Path is switched.
3274 3) Path has no mergeinfo of it's own but it's parent has mergeinfo with
3275 non-inheritable ranges (in this case the function will actually set
3276 override mergeinfo on the path if this isn't a dry-run and the merge
3277 is between differences in the same repository).
3278 4) Path has an immediate child (or children) missing from the WC because
3279 the child is switched or absent from the WC, or due to a sparse
3280 checkout.
3281 5) Path has a sibling (or siblings) missing from the WC because the
3282 sibling is switched, absent, schduled for deletion, or missing due to
3283 a sparse checkout.
3284 6) Path is absent from disk due to an authz restriction.
3285 7) Path is scheduled for deletion.
3286 8) Path is equal to MERGE_CMD_BATON->TARGET.
3288 Store the svn_client__merge_path_t *'s in *CHILDREN_WITH_MERGEINFO in
3289 depth-first order based on the svn_client__merge_path_t *s path member as
3290 sorted by svn_path_compare_paths().
3292 Note: Since the walk is rooted at MERGE_CMD_BATON->TARGET, the latter is
3293 guaranteed to be in *CHILDREN_WITH_MERGEINFO and due to the depth-first
3294 ordering it is guaranteed to be the first element in
3295 *CHILDREN_WITH_MERGEINFO.
3297 Cascade MERGE_SRC_CANON_PATH. */
3298 static svn_error_t *
3299 get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
3300 merge_cmd_baton_t *merge_cmd_baton,
3301 const char* merge_src_canon_path,
3302 const svn_wc_entry_t *entry,
3303 const char *source_root_url,
3304 const char *url1,
3305 const char *url2,
3306 svn_revnum_t revision1,
3307 svn_revnum_t revision2,
3308 svn_ra_session_t *ra_session,
3309 svn_wc_adm_access_t *adm_access,
3310 svn_client_ctx_t *ctx,
3311 svn_depth_t depth,
3312 apr_pool_t *pool)
3314 int i;
3315 apr_pool_t *iterpool;
3316 static const svn_wc_entry_callbacks2_t walk_callbacks =
3317 { get_mergeinfo_walk_cb, get_mergeinfo_error_handler };
3318 struct get_mergeinfo_walk_baton wb =
3319 { adm_access, children_with_mergeinfo,
3320 merge_src_canon_path, merge_cmd_baton->target, source_root_url,
3321 url1, url2, revision1, revision2,
3322 merge_cmd_baton->first_range, merge_cmd_baton->working_mergeinfo,
3323 merge_cmd_baton->long_pool, depth, ra_session, ctx };
3325 /* Cover cases 1), 2), 6), and 8) by walking the WC to get all paths which
3326 have mergeinfo and/or are switched or are absent from disk or is the
3327 target of the merge. */
3328 if (entry->kind == svn_node_file)
3329 SVN_ERR(walk_callbacks.found_entry(merge_cmd_baton->target, entry, &wb,
3330 pool));
3331 else
3332 SVN_ERR(svn_wc_walk_entries3(merge_cmd_baton->target, adm_access,
3333 &walk_callbacks, &wb, depth, TRUE,
3334 merge_cmd_baton->ctx->cancel_func,
3335 merge_cmd_baton->ctx->cancel_baton,
3336 pool));
3338 /* CHILDREN_WITH_MERGEINFO must be in depth first order, but
3339 svn_wc_walk_entries3() relies on svn_wc_entries_read() which means the
3340 paths at a given directory level are not in any particular order. Also,
3341 we may need to add elements to the array to cover case 3) through 5) from
3342 the docstring. If so, it is more efficient to find and insert these
3343 paths if the sibling paths are in a guaranteed depth-first order. For
3344 the first reason we sort the array, for the second reason we do it now
3345 rather than at the end of this function. */
3346 qsort(children_with_mergeinfo->elts,
3347 children_with_mergeinfo->nelts,
3348 children_with_mergeinfo->elt_size,
3349 compare_merge_path_t_as_paths);
3351 iterpool = svn_pool_create(pool);
3352 for (i = 0; i < children_with_mergeinfo->nelts; i++)
3354 int insert_index;
3355 svn_client__merge_path_t *child =
3356 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
3357 svn_pool_clear(iterpool);
3359 /* Case 3) Where merging to a path with a switched child the path gets
3360 non-inheritable mergeinfo for the merge range performed and the child
3361 gets it's own set of mergeinfo. If the switched child later
3362 "returns", e.g. a switched path is unswitched, the child may not have
3363 any explicit mergeinfo. If the initial merge is repeated we don't
3364 want to repeat the merge for the path, but we do want to repeat it
3365 for the previously switched child. To ensure this we check if all
3366 of CHILD's non-missing children have explicit mergeinfo (they should
3367 already be present in CHILDREN_WITH_MERGEINFO if they do). If not,
3368 add the children without mergeinfo to CHILDREN_WITH_MERGEINFO so
3369 do_directory_merge() will merge them independently.
3371 But that's not enough! Since do_directory_merge() performs
3372 the merges on the paths in CHILDREN_WITH_MERGEINFO in a depth first
3373 manner it will merge the previously switched path's parent first. As
3374 part of this merge it will update the parent's previously
3375 non-inheritable mergeinfo and make it inheritable (since it notices
3376 the path has no missing children), then when
3377 do_directory_merge() finally merges the previously missing
3378 child it needs to get mergeinfo from the child's nearest ancestor,
3379 but since do_directory_merge() already tweaked that
3380 mergeinfo, removing the non-inheritable flag, it appears that the
3381 child already has been merged to. To prevent this we set override
3382 mergeinfo on the child now, before any merging is done, so it has
3383 explicit mergeinfo that reflects only CHILD's inheritable
3384 mergeinfo. */
3386 if (child->has_noninheritable)
3388 apr_hash_t *entries;
3389 apr_hash_index_t *hi;
3390 svn_wc_adm_access_t *child_access;
3391 SVN_ERR(svn_wc_adm_probe_try3(&child_access, adm_access,
3392 child->path, TRUE, -1,
3393 merge_cmd_baton->ctx->cancel_func,
3394 merge_cmd_baton->ctx->cancel_baton,
3395 iterpool));
3396 SVN_ERR(svn_wc_entries_read(&entries, child_access, FALSE,
3397 iterpool));
3398 for (hi = apr_hash_first(iterpool, entries); hi;
3399 hi = apr_hash_next(hi))
3401 const void *key;
3402 svn_client__merge_path_t *child_of_noninheritable;
3403 const char *child_path;
3405 apr_hash_this(hi, &key, NULL, NULL);
3407 if (strcmp(key, SVN_WC_ENTRY_THIS_DIR) == 0)
3408 continue;
3410 /* Does this child already exist in CHILDREN_WITH_MERGEINFO? If
3411 not, create it and insert it into CHILDREN_WITH_MERGEINFO and
3412 set override mergeinfo on it. */
3413 child_path = svn_path_join(child->path, key, iterpool);
3414 insert_index = find_child_or_parent(children_with_mergeinfo,
3415 &child_of_noninheritable,
3416 child_path, TRUE, i,
3417 iterpool);
3418 if (!child_of_noninheritable)
3420 child_of_noninheritable =
3421 apr_pcalloc(children_with_mergeinfo->pool,
3422 sizeof(*child_of_noninheritable));
3423 child_of_noninheritable->path =
3424 apr_pstrdup(children_with_mergeinfo->pool, child_path);
3425 insert_child_to_merge(children_with_mergeinfo,
3426 child_of_noninheritable,
3427 insert_index);
3428 if (!merge_cmd_baton->dry_run
3429 && merge_cmd_baton->same_repos)
3431 svn_boolean_t inherited;
3432 svn_mergeinfo_t mergeinfo;
3433 SVN_ERR(svn_client__get_wc_mergeinfo
3434 (&mergeinfo, &inherited, FALSE,
3435 svn_mergeinfo_nearest_ancestor,
3436 entry, child_of_noninheritable->path,
3437 merge_cmd_baton->target, NULL, adm_access,
3438 merge_cmd_baton->ctx, iterpool));
3440 /* This child didn't have explicit working mergeinfo
3441 at the start of the merge. Make a note of that in
3442 our hash of working mergeinfo in the event this is
3443 a no-op merge. */
3444 if (merge_cmd_baton->first_range)
3446 working_mergeinfo_t *working_mergeinfo =
3447 apr_pcalloc(merge_cmd_baton->long_pool,
3448 sizeof(*working_mergeinfo));
3449 apr_hash_set(merge_cmd_baton->working_mergeinfo,
3450 child_of_noninheritable->path,
3451 APR_HASH_KEY_STRING,
3452 working_mergeinfo);
3455 SVN_ERR(svn_client__record_wc_mergeinfo(
3456 child_of_noninheritable->path, mergeinfo, adm_access,
3457 iterpool));
3462 /* Case 4, 5, and 7 are handled by the following function. */
3463 SVN_ERR(insert_parent_and_sibs_of_sw_absent_del_entry(
3464 children_with_mergeinfo, merge_cmd_baton, &i, child,
3465 adm_access, iterpool));
3466 } /* i < children_with_mergeinfo->nelts */
3468 svn_pool_destroy(iterpool);
3469 return SVN_NO_ERROR;
3473 /*-----------------------------------------------------------------------*/
3475 /*** Merge Source Normalization ***/
3477 typedef struct merge_source_t
3479 /* "left" side URL and revision (inclusive iff youngest) */
3480 const char *url1;
3481 svn_revnum_t rev1;
3483 /* "right" side URL and revision (inclusive iff youngest) */
3484 const char *url2;
3485 svn_revnum_t rev2;
3487 } merge_source_t;
3489 /* qsort-compatible sort routine, rating merge_source_t * objects to
3490 be in descending (youngest-to-oldest) order based on their ->rev1
3491 component. */
3492 static int
3493 compare_merge_source_ts(const void *a,
3494 const void *b)
3496 svn_revnum_t a_rev = ((const merge_source_t *)a)->rev1;
3497 svn_revnum_t b_rev = ((const merge_source_t *)b)->rev1;
3498 if (a_rev == b_rev)
3499 return 0;
3500 return a_rev < b_rev ? 1 : -1;
3503 /* Set *MERGE_SOURCE_TS_P to a list of merge sources generated by
3504 slicing history location SEGMENTS with a given requested merge
3505 RANGE. Use SOURCE_ROOT_URL for full source URL calculation.
3507 Order the merge sources in *MERGE_SOURCE_TS_P from oldest to
3508 youngest. */
3509 static svn_error_t *
3510 combine_range_with_segments(apr_array_header_t **merge_source_ts_p,
3511 svn_merge_range_t *range,
3512 apr_array_header_t *segments,
3513 const char *source_root_url,
3514 apr_pool_t *pool)
3516 apr_array_header_t *merge_source_ts =
3517 apr_array_make(pool, 1, sizeof(merge_source_t *));
3518 svn_revnum_t minrev = MIN(range->start, range->end) + 1;
3519 svn_revnum_t maxrev = MAX(range->start, range->end);
3520 svn_boolean_t subtractive = (range->start > range->end);
3521 int i;
3523 for (i = 0; i < segments->nelts; i++)
3525 svn_location_segment_t *segment =
3526 APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
3527 merge_source_t *merge_source;
3528 const char *path1 = NULL;
3529 svn_revnum_t rev1;
3531 /* If this segment doesn't overlap our range at all, or
3532 represents a gap, ignore it. */
3533 if ((segment->range_end < minrev)
3534 || (segment->range_start > maxrev)
3535 || (! segment->path))
3536 continue;
3538 /* If our range spans a segment boundary, we have to point our
3539 merge_source_t's path1 to the path of the immediately older
3540 segment, else it points to the same location as its path2. */
3541 rev1 = MAX(segment->range_start, minrev) - 1;
3542 if (minrev <= segment->range_start)
3544 if (i > 0)
3546 path1 = (APR_ARRAY_IDX(segments, i - 1,
3547 svn_location_segment_t *))->path;
3549 /* If we've backed PATH1 up into a segment gap, let's back
3550 it up further still to the segment before the gap. We'll
3551 have to adjust rev1, too. */
3552 if ((! path1) && (i > 1))
3554 path1 = (APR_ARRAY_IDX(segments, i - 2,
3555 svn_location_segment_t *))->path;
3556 rev1 = (APR_ARRAY_IDX(segments, i - 2,
3557 svn_location_segment_t *))->range_end;
3560 else
3562 path1 = apr_pstrdup(pool, segment->path);
3565 /* If we don't have two valid paths, we won't know what to do
3566 when merging. This could happen if someone requested a merge
3567 where the source didn't exist in a particular revision or
3568 something. The merge code would probably bomb out anyway, so
3569 we'll just *not* create a merge source in this case. */
3570 if (! (path1 && segment->path))
3571 continue;
3573 /* Build our merge source structure. */
3574 merge_source = apr_pcalloc(pool, sizeof(*merge_source));
3575 merge_source->url1 = svn_path_join(source_root_url,
3576 svn_path_uri_encode(path1,
3577 pool), pool);
3578 merge_source->url2 = svn_path_join(source_root_url,
3579 svn_path_uri_encode(segment->path,
3580 pool), pool);
3581 merge_source->rev1 = rev1;
3582 merge_source->rev2 = MIN(segment->range_end, maxrev);
3584 /* If this is subtractive, reverse the whole calculation. */
3585 if (subtractive)
3587 svn_revnum_t tmprev = merge_source->rev1;
3588 const char *tmpurl = merge_source->url1;
3589 merge_source->rev1 = merge_source->rev2;
3590 merge_source->url1 = merge_source->url2;
3591 merge_source->rev2 = tmprev;
3592 merge_source->url2 = tmpurl;
3595 APR_ARRAY_PUSH(merge_source_ts, merge_source_t *) = merge_source;
3598 /* If this was a subtractive merge, and we created more than one
3599 merge source, we need to reverse the sort ordering of our sources. */
3600 if (subtractive && (merge_source_ts->nelts > 1))
3601 qsort(merge_source_ts->elts, merge_source_ts->nelts,
3602 merge_source_ts->elt_size, compare_merge_source_ts);
3604 *merge_source_ts_p = merge_source_ts;
3605 return SVN_NO_ERROR;
3608 /* Set *MERGE_SOURCES to an array of merge_source_t * objects, each
3609 holding the paths and revisions needed to fully describe a range of
3610 requested merges; order the objects from oldest to youngest.
3612 Determine the requested merges by examining SOURCE (and its
3613 associated URL, SOURCE_URL) and PEG_REVISION (which specifies the
3614 line of history from which merges will be pulled) and
3615 RANGES_TO_MERGE (a list of svn_opt_revision_range_t's which provide
3616 revision ranges).
3618 If PEG_REVISION is unspecified, treat that it as HEAD.
3620 SOURCE_ROOT_URL is the root URL of the source repository.
3622 Use RA_SESSION -- whose session URL matches SOURCE_URL -- to answer
3623 historical questions.
3625 CTX is a client context baton.
3627 Use POOL for all allocation.
3629 See `MERGEINFO MERGE SOURCE NORMALIZATION' for more on the
3630 background of this function.
3632 static svn_error_t *
3633 normalize_merge_sources(apr_array_header_t **merge_sources_p,
3634 const char *source,
3635 const char *source_url,
3636 const char *source_root_url,
3637 const svn_opt_revision_t *peg_revision,
3638 const apr_array_header_t *ranges_to_merge,
3639 svn_ra_session_t *ra_session,
3640 svn_client_ctx_t *ctx,
3641 apr_pool_t *pool)
3643 svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
3644 svn_revnum_t peg_revnum;
3645 svn_revnum_t oldest_requested = SVN_INVALID_REVNUM;
3646 svn_revnum_t youngest_requested = SVN_INVALID_REVNUM;
3647 svn_revnum_t trim_revision = SVN_INVALID_REVNUM;
3648 svn_opt_revision_t youngest_opt_rev;
3649 apr_array_header_t *merge_range_ts, *segments;
3650 apr_pool_t *subpool;
3651 int i;
3652 youngest_opt_rev.kind = svn_opt_revision_head;
3654 /* Initialize our return variable. */
3655 *merge_sources_p = apr_array_make(pool, 1, sizeof(merge_source_t *));
3657 /* Resolve our PEG_REVISION to a real number. */
3658 SVN_ERR(svn_client__get_revision_number(&peg_revnum, &youngest_rev,
3659 ra_session, peg_revision,
3660 source, pool));
3661 if (! SVN_IS_VALID_REVNUM(peg_revnum))
3662 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
3664 /* Create a list to hold svn_merge_range_t's. */
3665 merge_range_ts = apr_array_make(pool, ranges_to_merge->nelts,
3666 sizeof(svn_merge_range_t *));
3668 subpool = svn_pool_create(pool);
3669 for (i = 0; i < ranges_to_merge->nelts; i++)
3671 svn_revnum_t range_start_rev, range_end_rev;
3672 svn_opt_revision_t *range_start =
3673 &((APR_ARRAY_IDX(ranges_to_merge, i,
3674 svn_opt_revision_range_t *))->start);
3675 svn_opt_revision_t *range_end =
3676 &((APR_ARRAY_IDX(ranges_to_merge, i,
3677 svn_opt_revision_range_t *))->end);
3679 svn_pool_clear(subpool);
3681 /* Resolve revisions to real numbers, validating as we go. */
3682 if ((range_start->kind == svn_opt_revision_unspecified)
3683 || (range_end->kind == svn_opt_revision_unspecified))
3684 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
3685 _("Not all required revisions are specified"));
3686 SVN_ERR(svn_client__get_revision_number(&range_start_rev, &youngest_rev,
3687 ra_session, range_start,
3688 source, subpool));
3689 SVN_ERR(svn_client__get_revision_number(&range_end_rev, &youngest_rev,
3690 ra_session, range_end,
3691 source, subpool));
3693 /* If this isn't a no-op range... */
3694 if (range_start_rev != range_end_rev)
3696 /* ...then create an svn_merge_range_t object for it. */
3697 svn_merge_range_t *range = apr_pcalloc(pool, sizeof(*range));
3698 range->start = range_start_rev;
3699 range->end = range_end_rev;
3700 range->inheritable = TRUE;
3702 /* Add our merge range to our list thereof. */
3703 APR_ARRAY_PUSH(merge_range_ts, svn_merge_range_t *) = range;
3707 /* No ranges to merge? No problem. */
3708 if (merge_range_ts->nelts == 0)
3709 return SVN_NO_ERROR;
3711 /* Find the extremes of the revisions across our set of ranges. */
3712 for (i = 0; i < merge_range_ts->nelts; i++)
3714 svn_merge_range_t *range =
3715 APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *);
3716 svn_revnum_t minrev = MIN(range->start, range->end);
3717 svn_revnum_t maxrev = MAX(range->start, range->end);
3719 /* Keep a running tally of the oldest and youngest requested
3720 revisions. */
3721 if ((! SVN_IS_VALID_REVNUM(oldest_requested))
3722 || (minrev < oldest_requested))
3723 oldest_requested = minrev;
3724 if ((! SVN_IS_VALID_REVNUM(youngest_requested))
3725 || (maxrev > youngest_requested))
3726 youngest_requested = maxrev;
3729 /* ### FIXME: Our underlying APIs can't yet handle the case where
3730 the peg revision isn't the youngest of the three revisions. So
3731 we'll just verify that the source in the peg revision is related
3732 to the the source in the youngest requested revision (which is
3733 all the underlying APIs would do in this case right now anyway). */
3734 if (peg_revnum < youngest_requested)
3736 const char *start_url;
3737 svn_opt_revision_t requested, unspec, pegrev, *start_revision;
3738 unspec.kind = svn_opt_revision_unspecified;
3739 requested.kind = svn_opt_revision_number;
3740 requested.value.number = youngest_requested;
3741 pegrev.kind = svn_opt_revision_number;
3742 pegrev.value.number = peg_revnum;
3744 SVN_ERR(svn_client__repos_locations(&start_url, &start_revision,
3745 NULL, NULL,
3746 ra_session, source_url,
3747 &pegrev, &requested,
3748 &unspec, ctx, pool));
3749 peg_revnum = youngest_requested;
3752 /* Fetch the locations for our merge range span. */
3753 SVN_ERR(svn_client__repos_location_segments(&segments,
3754 ra_session, "",
3755 peg_revnum,
3756 youngest_requested,
3757 oldest_requested,
3758 ctx, pool));
3760 /* See if we fetched enough history to do the job. "Surely we did,"
3761 you say. "After all, we covered the entire requested merge
3762 range." Yes, that's true, but if our first segment doesn't
3763 extend back to the oldest request revision, we've got a special
3764 case to deal with. Or if the first segment represents a gap,
3765 that's another special case. */
3766 trim_revision = SVN_INVALID_REVNUM;
3767 if (segments->nelts)
3769 svn_location_segment_t *segment =
3770 APR_ARRAY_IDX(segments, 0, svn_location_segment_t *);
3772 /* If the first segment doesn't start with the OLDEST_REQUESTED
3773 revision, we'll need to pass a trim revision to our range
3774 cruncher. */
3775 if (segment->range_start != oldest_requested)
3777 trim_revision = segment->range_start;
3780 /* Else, if the first segment has no path (and therefore is a
3781 gap), then we'll fetch the copy source revision from the
3782 second segment (provided there is one, of course) and use it
3783 to prepend an extra pathful segment to our list.
3785 ### We could avoid this bit entirely if we'd passed
3786 ### SVN_INVALID_REVNUM instead of OLDEST_REQUESTED to
3787 ### svn_client__repos_location_segments(), but that would
3788 ### really penalize clients hitting pre-1.5 repositories with
3789 ### the typical small merge range request (because of the
3790 ### lack of a node-origins cache in the repository). */
3791 else if (! segment->path)
3793 if (segments->nelts > 1)
3795 svn_location_segment_t *segment2 =
3796 APR_ARRAY_IDX(segments, 1, svn_location_segment_t *);
3797 const char *copyfrom_path, *segment_url;
3798 svn_revnum_t copyfrom_rev;
3799 svn_opt_revision_t range_start_rev;
3800 range_start_rev.kind = svn_opt_revision_number;
3801 range_start_rev.value.number = segment2->range_start;
3803 segment_url = svn_path_url_add_component(source_root_url,
3804 segment2->path, pool);
3805 SVN_ERR(svn_client__get_copy_source(segment_url,
3806 &range_start_rev,
3807 &copyfrom_path,
3808 &copyfrom_rev,
3809 ctx, pool));
3810 /* Got copyfrom data? Fix up the first segment to cover
3811 back to COPYFROM_REV + 1, and then prepend a new
3812 segment covering just COPYFROM_REV. */
3813 if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev))
3815 svn_location_segment_t *new_segment =
3816 apr_pcalloc(pool, sizeof(*new_segment));
3817 /* Skip the leading '/'. */
3818 new_segment->path = (*copyfrom_path == '/')
3819 ? copyfrom_path + 1 : copyfrom_path;
3820 new_segment->range_start = copyfrom_rev;
3821 new_segment->range_end = copyfrom_rev;
3822 segment->range_start = copyfrom_rev + 1;
3823 APR_ARRAY_PUSH(segments, svn_location_segment_t *) = NULL;
3824 memmove(segments->elts + segments->elt_size,
3825 segments->elts,
3826 segments->elt_size * (segments->nelts - 1));
3827 APR_ARRAY_IDX(segments, 0, svn_location_segment_t *) =
3828 new_segment;
3834 /* For each range in our requested range set, try to determine the
3835 path(s) associated with that range. */
3836 for (i = 0; i < merge_range_ts->nelts; i++)
3838 svn_merge_range_t *range =
3839 APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *);
3840 apr_array_header_t *merge_sources;
3841 int j;
3843 if (SVN_IS_VALID_REVNUM(trim_revision))
3845 /* If the youngest of the range revisions predates the trim
3846 revision, discard the range. */
3847 if (MAX(range->start, range->end) < trim_revision)
3848 continue;
3850 /* Otherwise, if either of oldest of the range revisions predates
3851 the trim revision, update the range revision to be equal
3852 to the trim revision. */
3853 if (range->start < trim_revision)
3854 range->start = trim_revision;
3855 if (range->end < trim_revision)
3856 range->end = trim_revision;
3859 /* Copy the resulting merge sources into master list thereof. */
3860 SVN_ERR(combine_range_with_segments(&merge_sources, range,
3861 segments, source_root_url, pool));
3862 for (j = 0; j < merge_sources->nelts; j++)
3864 APR_ARRAY_PUSH(*merge_sources_p, merge_source_t *) =
3865 APR_ARRAY_IDX(merge_sources, j, merge_source_t *);
3869 return SVN_NO_ERROR;
3873 /*-----------------------------------------------------------------------*/
3875 /*** Merge Workhorse Functions ***/
3877 /* The single-file, simplified version of do_directory_merge(), which see for
3878 parameter descriptions.
3880 Additional parameters:
3882 If SOURCES_RELATED is set, the "left" and "right" sides of the
3883 merge source are historically related (ancestors, uncles, second
3884 cousins thrice removed, etc...). (This is used to simulate the
3885 history checks that the repository logic does in the directory case.)
3887 static svn_error_t *
3888 do_file_merge(const char *url1,
3889 svn_revnum_t revision1,
3890 const char *url2,
3891 svn_revnum_t revision2,
3892 const char *target_wcpath,
3893 svn_boolean_t sources_related,
3894 svn_wc_adm_access_t *adm_access,
3895 notification_receiver_baton_t *notify_b,
3896 merge_cmd_baton_t *merge_b,
3897 apr_pool_t *pool)
3899 svn_error_t *err = SVN_NO_ERROR;
3900 apr_hash_t *props1, *props2;
3901 const char *tmpfile1, *tmpfile2;
3902 const char *mimetype1, *mimetype2;
3903 svn_string_t *pval;
3904 apr_array_header_t *propchanges, *remaining_ranges;
3905 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
3906 svn_wc_notify_state_t text_state = svn_wc_notify_state_unknown;
3907 svn_client_ctx_t *ctx = merge_b->ctx;
3908 const char *mergeinfo_path;
3909 svn_merge_range_t range;
3910 svn_mergeinfo_t target_mergeinfo;
3911 const svn_wc_entry_t *entry;
3912 int i;
3913 svn_boolean_t indirect = FALSE;
3914 apr_pool_t *subpool;
3915 svn_boolean_t is_rollback = (revision1 > revision2);
3916 const char *primary_url = is_rollback ? url1 : url2;
3917 svn_boolean_t honor_mergeinfo, record_mergeinfo;
3919 mergeinfo_behavior(&honor_mergeinfo, &record_mergeinfo, merge_b);
3921 /* Note that this is a single-file merge. */
3922 notify_b->is_single_file_merge = TRUE;
3924 /* Ensure that the adm_access we're playing with is our TARGET_WCPATH's
3925 parent, as required by some of underlying helper functions. */
3926 SVN_ERR(svn_wc_adm_probe_try3(&adm_access, adm_access, target_wcpath,
3927 TRUE, -1, merge_b->ctx->cancel_func,
3928 merge_b->ctx->cancel_baton,
3929 pool));
3931 SVN_ERR(svn_wc__entry_versioned(&entry, target_wcpath, adm_access, FALSE,
3932 pool));
3934 range.start = revision1;
3935 range.end = revision2;
3936 range.inheritable = TRUE;
3937 if (honor_mergeinfo)
3939 const char *source_root_url;
3940 svn_mergeinfo_t implicit_mergeinfo;
3942 /* Fetch mergeinfo (temporarily reparenting ra_session1 to
3943 working copy target URL). */
3944 SVN_ERR(svn_ra_reparent(merge_b->ra_session1, entry->url, pool));
3945 SVN_ERR(get_full_mergeinfo(&target_mergeinfo, &implicit_mergeinfo,
3946 entry, &indirect, svn_mergeinfo_inherited,
3947 merge_b->ra_session1, target_wcpath,
3948 MAX(revision1, revision2),
3949 MIN(revision1, revision2),
3950 adm_access, ctx, pool));
3952 if (merge_b->first_range)
3954 working_mergeinfo_t *working_mergeinfo =
3955 apr_pcalloc(merge_b->long_pool, sizeof(*working_mergeinfo));
3956 if (!indirect && target_mergeinfo)
3958 svn_string_t *mergeinfo_string;
3959 SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string,
3960 target_mergeinfo, pool));
3961 working_mergeinfo->working_mergeinfo_propval =
3962 svn_string_dup(mergeinfo_string, merge_b->long_pool);
3964 apr_hash_set(merge_b->working_mergeinfo, target_wcpath,
3965 APR_HASH_KEY_STRING, working_mergeinfo);
3968 SVN_ERR(svn_ra_reparent(merge_b->ra_session1, url1, pool));
3970 /* Calculate remaining merges. */
3971 SVN_ERR(svn_ra_get_repos_root2(merge_b->ra_session1,
3972 &source_root_url, pool));
3973 SVN_ERR(svn_client__path_relative_to_root(&mergeinfo_path, primary_url,
3974 source_root_url, TRUE, NULL,
3975 NULL, pool));
3976 SVN_ERR(calculate_remaining_ranges(&remaining_ranges, source_root_url,
3977 url1, revision1, url2, revision2,
3978 TRUE, target_mergeinfo,
3979 implicit_mergeinfo,
3980 merge_b->ra_session1,
3981 entry, ctx, pool));
3983 else
3985 remaining_ranges = apr_array_make(pool, 1, sizeof(&range));
3986 APR_ARRAY_PUSH(remaining_ranges, svn_merge_range_t *) = &range;
3989 subpool = svn_pool_create(pool);
3991 for (i = 0; i < remaining_ranges->nelts; i++)
3993 svn_wc_notify_t *n;
3994 svn_boolean_t header_sent = FALSE;
3996 /* When using this merge range, account for the exclusivity of
3997 its low value (which is indicated by this operation being a
3998 merge vs. revert). */
3999 svn_merge_range_t *r = APR_ARRAY_IDX(remaining_ranges, i,
4000 svn_merge_range_t *);
4002 svn_pool_clear(subpool);
4004 n = svn_wc_create_notify(target_wcpath,
4005 svn_wc_notify_merge_begin,
4006 subpool);
4007 if (merge_b->sources_ancestral)
4008 n->merge_range = r;
4010 /* While we currently don't allow it, in theory we could be
4011 fetching two fulltexts from two different repositories here. */
4012 SVN_ERR(single_file_merge_get_file(&tmpfile1, merge_b->ra_session1,
4013 &props1, r->start, target_wcpath,
4014 subpool));
4015 SVN_ERR(single_file_merge_get_file(&tmpfile2, merge_b->ra_session2,
4016 &props2, r->end, target_wcpath,
4017 subpool));
4019 /* Discover any svn:mime-type values in the proplists */
4020 pval = apr_hash_get(props1, SVN_PROP_MIME_TYPE,
4021 strlen(SVN_PROP_MIME_TYPE));
4022 mimetype1 = pval ? pval->data : NULL;
4024 pval = apr_hash_get(props2, SVN_PROP_MIME_TYPE,
4025 strlen(SVN_PROP_MIME_TYPE));
4026 mimetype2 = pval ? pval->data : NULL;
4028 /* Deduce property diffs. */
4029 SVN_ERR(svn_prop_diffs(&propchanges, props2, props1, subpool));
4031 /* If we aren't ignoring ancestry, then we've already done
4032 ancestry relatedness checks. If we are ignoring ancestry, or
4033 our sources are known to be related, then we can do
4034 text-n-props merge; otherwise, we have to do a delete-n-add
4035 merge. */
4036 if (! (merge_b->ignore_ancestry || sources_related))
4038 /* Delete... */
4039 SVN_ERR(merge_file_deleted(adm_access,
4040 &text_state,
4041 target_wcpath,
4042 NULL,
4043 NULL,
4044 mimetype1, mimetype2,
4045 props1,
4046 merge_b));
4047 single_file_merge_notify(notify_b, target_wcpath,
4048 svn_wc_notify_update_delete, text_state,
4049 svn_wc_notify_state_unknown, n,
4050 &header_sent, subpool);
4052 /* ...plus add... */
4053 SVN_ERR(merge_file_added(adm_access,
4054 &text_state, &prop_state,
4055 target_wcpath,
4056 tmpfile1,
4057 tmpfile2,
4058 r->start,
4059 r->end,
4060 mimetype1, mimetype2,
4061 propchanges, props1,
4062 merge_b));
4063 single_file_merge_notify(notify_b, target_wcpath,
4064 svn_wc_notify_update_add, text_state,
4065 prop_state, n, &header_sent, subpool);
4066 /* ... equals replace. */
4068 else
4070 SVN_ERR(merge_file_changed(adm_access,
4071 &text_state, &prop_state,
4072 target_wcpath,
4073 tmpfile1,
4074 tmpfile2,
4075 r->start,
4076 r->end,
4077 mimetype1, mimetype2,
4078 propchanges, props1,
4079 merge_b));
4080 single_file_merge_notify(notify_b, target_wcpath,
4081 svn_wc_notify_update_update, text_state,
4082 prop_state, n, &header_sent, subpool);
4085 /* Ignore if temporary file not found. It may have been renamed. */
4086 /* (This is where we complain about missing Lisp, or better yet,
4087 Python...) */
4088 err = svn_io_remove_file(tmpfile1, subpool);
4089 if (err && ! APR_STATUS_IS_ENOENT(err->apr_err))
4090 return err;
4091 svn_error_clear(err);
4092 err = SVN_NO_ERROR;
4093 err = svn_io_remove_file(tmpfile2, subpool);
4094 if (err && ! APR_STATUS_IS_ENOENT(err->apr_err))
4095 return err;
4096 svn_error_clear(err);
4097 err = SVN_NO_ERROR;
4099 if (i < remaining_ranges->nelts - 1 &&
4100 is_path_conflicted_by_merge(merge_b))
4102 err = make_merge_conflict_error(target_wcpath, r, pool);
4103 break;
4107 /* Record updated WC mergeinfo to account for our new merges, minus
4108 any unresolved conflicts and skips. */
4109 if (record_mergeinfo && remaining_ranges->nelts)
4111 apr_hash_t *merges;
4112 SVN_ERR(determine_merges_performed(&merges, target_wcpath,
4113 &range, svn_depth_infinity,
4114 adm_access, notify_b, merge_b,
4115 subpool));
4116 /* If this whole merge was simply a no-op merge to a file then
4117 we don't touch the local mergeinfo. */
4118 if (merge_b->operative_merge)
4120 /* If merge target has indirect mergeinfo set it before
4121 recording the first merge range. */
4122 if (indirect)
4123 SVN_ERR(svn_client__record_wc_mergeinfo(target_wcpath,
4124 target_mergeinfo,
4125 adm_access, subpool));
4127 SVN_ERR(update_wc_mergeinfo(target_wcpath, entry, mergeinfo_path,
4128 merges, is_rollback, adm_access,
4129 ctx, subpool));
4133 svn_pool_destroy(subpool);
4135 /* Sleep to ensure timestamp integrity. */
4136 svn_sleep_for_timestamps();
4138 return err;
4142 /* Perform a merge of changes between URL1@REVISION1 and
4143 URL2@REVISION2, applied to the children of PARENT_ENTRY. URL1,
4144 URL2, and PARENT_ENTRY all represent directories -- for the single
4145 file case, the caller should use do_file_merge().
4147 If MERGE_B->sources_ancestral is set, then URL1@REVISION1 must be a
4148 historical ancestor of URL2@REVISION2, or vice-versa (see
4149 `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements around
4150 the values of URL1, REVISION1, URL2, and REVISION2 in this case).
4152 Handle DEPTH as documented for svn_client_merge3().
4154 CHILDREN_WITH_MERGEINFO may contain child paths (svn_client__merge_path_t *)
4155 which are switched or which have mergeinfo which differs from that of the
4156 merge target root (ignored if empty or NULL). CHILDREN_WITH_MERGEINFO
4157 list should have entries sorted in depth first order as mandated by the
4158 reporter API. Because of this, we drive the diff editor in such a way that
4159 it avoids merging child paths when a merge is driven for their parent path.
4161 CHILDREN_WITH_MERGEINFO may contain TARGET_WCPATH (which may be
4162 MERGE_B->TARGET), in that case TARGET_INDEX is the array index for
4163 TARGET_WCPATH, otherwise it should be set to a negative value.
4165 NOTE: This is a wrapper around drive_merge_report_editor() which
4166 handles the complexities inherent to situations where a given
4167 directory's children may have intersecting merges (because they
4168 meet one or more of the criteria described in get_mergeinfo_paths()).
4170 static svn_error_t *
4171 do_directory_merge(const char *url1,
4172 svn_revnum_t revision1,
4173 const char *url2,
4174 svn_revnum_t revision2,
4175 const svn_wc_entry_t *parent_entry,
4176 svn_wc_adm_access_t *adm_access,
4177 svn_depth_t depth,
4178 notification_receiver_baton_t *notify_b,
4179 merge_cmd_baton_t *merge_b,
4180 apr_pool_t *pool)
4182 svn_error_t *err = SVN_NO_ERROR;
4183 apr_array_header_t *children_with_mergeinfo;
4184 int merge_target_len = strlen(merge_b->target);
4185 int i;
4186 svn_merge_range_t range;
4187 svn_ra_session_t *ra_session;
4188 svn_boolean_t inheritable;
4189 apr_pool_t *iterpool;
4190 const char *target_wcpath = svn_wc_adm_access_path(adm_access);
4191 svn_client__merge_path_t *target_merge_path;
4192 svn_boolean_t is_rollback = (revision1 > revision2);
4193 const char *primary_url = is_rollback ? url1 : url2;
4194 const char *source_root_url, *mergeinfo_path;
4195 svn_boolean_t honor_mergeinfo, record_mergeinfo;
4196 svn_boolean_t same_urls = (strcmp(url1, url2) == 0);
4198 mergeinfo_behavior(&honor_mergeinfo, &record_mergeinfo, merge_b);
4200 /* Initialize CHILDREN_WITH_MERGEINFO. */
4201 children_with_mergeinfo =
4202 apr_array_make(pool, 0, sizeof(svn_client__merge_path_t *));
4203 notify_b->children_with_mergeinfo = children_with_mergeinfo;
4205 /* If our merge sources aren't related to each other, or don't come
4206 from the same repository as our target, mergeinfo is meaningless
4207 and we can skip right to the business of merging changes! We'll
4208 just drop a dummy item into CHILDREN_WITH_MERGEINFO if the merge
4209 sources are related. */
4210 if (! (merge_b->sources_ancestral && merge_b->same_repos))
4212 if (merge_b->sources_ancestral)
4214 svn_client__merge_path_t *item = apr_pcalloc(pool, sizeof(*item));
4215 svn_merge_range_t *itemrange = apr_pcalloc(pool, sizeof(*itemrange));
4216 apr_array_header_t *remaining_ranges =
4217 apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
4219 itemrange->start = revision1;
4220 itemrange->end = revision2;
4221 itemrange->inheritable = TRUE;
4222 APR_ARRAY_PUSH(remaining_ranges, svn_merge_range_t *) = itemrange;
4224 item->path = apr_pstrdup(pool, target_wcpath);
4225 item->remaining_ranges = remaining_ranges;
4226 APR_ARRAY_PUSH(children_with_mergeinfo,
4227 svn_client__merge_path_t *) = item;
4229 return drive_merge_report_editor(target_wcpath,
4230 url1, revision1, url2, revision2,
4231 NULL, is_rollback, depth, notify_b,
4232 adm_access, &merge_callbacks,
4233 merge_b, pool);
4236 /*** If we get here, we're dealing with related sources from the
4237 same repository as the target -- merge tracking might be
4238 happenin'! ***/
4240 /* Point our RA_SESSION to the URL of our youngest merge source side. */
4241 ra_session = is_rollback ? merge_b->ra_session1 : merge_b->ra_session2;
4243 /* Fill CHILDREN_WITH_MERGEINFO with child paths (const
4244 svn_client__merge_path_t *) which might have intersecting merges
4245 because they meet one or more of the criteria described in
4246 get_mergeinfo_paths(). Here the paths are arranged in a depth
4247 first order. */
4248 SVN_ERR(svn_ra_get_repos_root2(ra_session, &source_root_url, pool));
4249 SVN_ERR(svn_client__path_relative_to_root(&mergeinfo_path, primary_url,
4250 source_root_url, TRUE, NULL,
4251 NULL, pool));
4252 SVN_ERR(get_mergeinfo_paths(children_with_mergeinfo, merge_b,
4253 mergeinfo_path, parent_entry, source_root_url,
4254 url1, url2, revision1, revision2,
4255 ra_session, adm_access,
4256 merge_b->ctx, depth, pool));
4258 /* The first item from the CHILDREN_WITH_MERGEINFO is the target
4259 thanks to depth-first ordering. */
4260 target_merge_path = APR_ARRAY_IDX(children_with_mergeinfo, 0,
4261 svn_client__merge_path_t *);
4262 merge_b->target_missing_child = target_merge_path->missing_child;
4263 inheritable = ((! merge_b->target_missing_child)
4264 && ((depth == svn_depth_infinity)
4265 || (depth == svn_depth_immediates)));
4267 /* If we are honoring mergeinfo, then for each item in
4268 CHILDREN_WITH_MERGEINFO, we need to calculate what needs to be
4269 merged, and then merge it. Otherwise, we just merge what we were
4270 asked to merge across the whole tree. */
4271 SVN_ERR(populate_remaining_ranges(children_with_mergeinfo,
4272 source_root_url,
4273 url1, revision1, url2, revision2,
4274 inheritable, honor_mergeinfo,
4275 ra_session, mergeinfo_path,
4276 adm_access, merge_b));
4278 if (honor_mergeinfo)
4280 svn_revnum_t start_rev, end_rev;
4282 /* From the remaining ranges of each item in
4283 CHILDREN_WITH_MERGEINFO, pick the most inclusive start and
4284 end revisions. */
4285 start_rev = get_most_inclusive_start_rev(children_with_mergeinfo,
4286 is_rollback);
4287 if (start_rev == SVN_INVALID_REVNUM)
4288 start_rev = revision1;
4290 end_rev = get_youngest_end_rev(children_with_mergeinfo, is_rollback);
4292 /* Build a range which describes our most inclusive merge. */
4293 range.start = start_rev;
4294 range.end = revision2;
4295 range.inheritable = inheritable;
4297 /* While END_REV is valid, do the following:
4299 1. slice each remaining ranges around this 'end_rev'.
4300 2. starting with START_REV, call
4301 drive_merge_report_editor() on MERGE_B->target for
4302 start_rev:end_rev.
4303 3. remove the first item from each remaining range.
4304 4. set START_REV=END_REV and pick the next END_REV.
4305 5. lather, rinse, repeat.
4307 iterpool = svn_pool_create(pool);
4308 while (end_rev != SVN_INVALID_REVNUM)
4310 svn_revnum_t next_end_rev;
4311 const char *real_url1 = url1, *real_url2 = url2;
4312 const char *old_sess1_url = NULL, *old_sess2_url = NULL;
4314 svn_pool_clear(iterpool);
4316 /* Use persistent pool while playing with remaining_ranges. */
4317 slice_remaining_ranges(children_with_mergeinfo, is_rollback,
4318 end_rev, pool);
4319 notify_b->cur_ancestor_index = -1;
4321 /* URL1@REVISION1 is a real location; URL2@REVISION2 is a
4322 real location -- that much we know (thanks to the merge
4323 source normalization code). But for revisions between
4324 them, the URLs might differ. Here are the rules:
4326 * If URL1 == URL2, then all URLs between REVISION1 and
4327 REVISION2 also match URL1/URL2.
4329 * If URL1 != URL2, then:
4331 * If REVISION1 < REVISION2, only REVISION1 maps to
4332 URL1. The revisions between REVISION1+1 and
4333 REVISION2 (inclusive) map to URL2.
4335 * If REVISION1 > REVISION2, Only REVISION2 maps to
4336 URL2. The revisions between REVISION1 and
4337 REVISION2+1 (inclusive) map to URL1.
4339 We need to adjust our URLs accordingly, here.
4341 if (! same_urls)
4343 if (is_rollback && (end_rev != revision2))
4345 real_url2 = url1;
4346 SVN_ERR(svn_client__ensure_ra_session_url
4347 (&old_sess2_url, merge_b->ra_session2,
4348 real_url2, iterpool));
4350 if ((! is_rollback) && (start_rev != revision1))
4352 real_url1 = url2;
4353 SVN_ERR(svn_client__ensure_ra_session_url
4354 (&old_sess1_url, merge_b->ra_session1,
4355 real_url1, iterpool));
4358 SVN_ERR(drive_merge_report_editor(merge_b->target,
4359 real_url1, start_rev, real_url2,
4360 end_rev, children_with_mergeinfo,
4361 is_rollback,
4362 depth, notify_b, adm_access,
4363 &merge_callbacks, merge_b,
4364 iterpool));
4365 if (old_sess1_url)
4366 SVN_ERR(svn_ra_reparent(merge_b->ra_session1,
4367 old_sess1_url, iterpool));
4368 if (old_sess2_url)
4369 SVN_ERR(svn_ra_reparent(merge_b->ra_session2,
4370 old_sess2_url, iterpool));
4372 /* Prepare for the next iteration (if any). */
4373 remove_first_range_from_remaining_ranges(children_with_mergeinfo,
4374 pool);
4375 next_end_rev = get_youngest_end_rev(children_with_mergeinfo,
4376 is_rollback);
4377 if ((next_end_rev != SVN_INVALID_REVNUM)
4378 && is_path_conflicted_by_merge(merge_b))
4380 svn_merge_range_t conflicted_range;
4381 conflicted_range.start = start_rev;
4382 conflicted_range.end = end_rev;
4383 err = make_merge_conflict_error(merge_b->target,
4384 &conflicted_range, pool);
4385 range.end = end_rev;
4386 break;
4388 start_rev = end_rev;
4389 end_rev = next_end_rev;
4391 svn_pool_destroy(iterpool);
4393 else
4395 /* Build a range which describes our most inclusive merge. */
4396 range.start = revision1;
4397 range.end = revision2;
4398 range.inheritable = inheritable;
4400 /* Reset cur_ancestor_index to -1 so that subsequent cherry
4401 picked revision ranges will be notified upon subsequent
4402 operative merge. */
4403 notify_b->cur_ancestor_index = -1;
4405 SVN_ERR(drive_merge_report_editor(merge_b->target,
4406 url1, revision1, url2, revision2,
4407 NULL, is_rollback,
4408 depth, notify_b, adm_access,
4409 &merge_callbacks, merge_b,
4410 pool));
4413 /* Record mergeinfo where appropriate.
4415 NOTE: any paths in CHILDREN_WITH_MERGEINFO which were switched
4416 but had no explicit working mergeinfo at the start of the call,
4417 will have some at the end of it if merge is not a no-op merge.
4419 iterpool = svn_pool_create(pool);
4420 if (record_mergeinfo)
4422 /* Update the WC mergeinfo here to account for our new
4423 merges, minus any unresolved conflicts and skips. */
4424 apr_hash_t *merges;
4426 /* Remove absent children at or under TARGET_WCPATH from
4427 NOTIFY_B->SKIPPED_PATHS and CHILDREN_WITH_MERGEINFO before we
4428 calculate the merges performed. */
4429 remove_absent_children(merge_b->target,
4430 children_with_mergeinfo, notify_b);
4431 SVN_ERR(determine_merges_performed(&merges, merge_b->target, &range,
4432 depth, adm_access, notify_b, merge_b,
4433 iterpool));
4434 SVN_ERR(record_mergeinfo_on_merged_children(depth, adm_access, notify_b,
4435 merge_b, iterpool));
4436 SVN_ERR(update_wc_mergeinfo(merge_b->target, parent_entry,
4437 mergeinfo_path, merges,
4438 is_rollback, adm_access, merge_b->ctx,
4439 iterpool));
4440 for (i = 0; i < children_with_mergeinfo->nelts; i++)
4442 const char *child_repos_path;
4443 const char *child_merge_src_canon_path;
4444 const svn_wc_entry_t *child_entry;
4445 apr_array_header_t *child_merge_rangelist;
4446 apr_hash_t *child_merges;
4447 svn_merge_range_t *child_merge_range;
4448 svn_client__merge_path_t *child =
4449 APR_ARRAY_IDX(children_with_mergeinfo, i,
4450 svn_client__merge_path_t *);
4451 if (!child || child->absent)
4452 continue;
4454 if (strlen(child->path) == merge_target_len)
4455 child_repos_path = "";
4456 else
4457 child_repos_path = child->path +
4458 (merge_target_len ? merge_target_len + 1 : 0);
4459 child_merge_src_canon_path = svn_path_join(mergeinfo_path,
4460 child_repos_path,
4461 iterpool);
4462 SVN_ERR(svn_wc__entry_versioned(&child_entry, child->path,
4463 adm_access, FALSE, iterpool));
4465 child_merges = apr_hash_make(iterpool);
4466 child_merge_range = svn_merge_range_dup(&range, iterpool);
4467 if (child_entry->kind == svn_node_file)
4468 child_merge_range->inheritable = TRUE;
4469 else
4470 child_merge_range->inheritable =
4471 (!(child->missing_child)
4472 && (depth == svn_depth_infinity
4473 || depth == svn_depth_immediates));
4474 child_merge_rangelist =
4475 apr_array_make(iterpool, 1,
4476 sizeof(child_merge_range));
4477 APR_ARRAY_PUSH(child_merge_rangelist,
4478 svn_merge_range_t *) = child_merge_range;
4479 apr_hash_set(child_merges, child->path, APR_HASH_KEY_STRING,
4480 child_merge_rangelist);
4481 /* If merge target has indirect mergeinfo set it before
4482 recording the first merge range. */
4483 if (child->indirect_mergeinfo)
4485 SVN_ERR(svn_client__record_wc_mergeinfo(
4486 child->path,
4487 child->pre_merge_mergeinfo,
4488 adm_access,
4489 iterpool));
4491 SVN_ERR(update_wc_mergeinfo(child->path, child_entry,
4492 child_merge_src_canon_path,
4493 child_merges, is_rollback,
4494 adm_access, merge_b->ctx, iterpool));
4496 SVN_ERR(mark_mergeinfo_as_inheritable_for_a_range(
4497 child->pre_merge_mergeinfo,
4498 TRUE,
4499 &range,
4500 child_merge_src_canon_path,
4501 child->path,
4502 adm_access,
4503 merge_b,
4504 children_with_mergeinfo,
4505 i, iterpool));
4506 if (i > 0)
4507 SVN_ERR(svn_client__elide_mergeinfo(child->path, merge_b->target,
4508 child_entry, adm_access,
4509 merge_b->ctx, iterpool));
4510 } /* (i = 0; i < children_with_mergeinfo->nelts; i++) */
4512 /* If a path has an immediate parent with non-inheritable mergeinfo at
4513 this point, then it meets criteria 3 or 5 described in
4514 get_mergeinfo_paths' doc string. For paths which exist prior to a
4515 merge explicit mergeinfo has already been set. But for paths added
4516 during the merge this is not the case. The path might have explicit
4517 mergeinfo from the merge source, but no mergeinfo yet exists
4518 describing *this* merge. So the added path has either incomplete
4519 explicit mergeinfo or inherits incomplete mergeinfo from its
4520 immediate parent (if any, the parent might have only non-inheritable
4521 ranges in which case the path simply inherits empty mergeinfo).
4523 So here we look at the root path of each subtree added during the
4524 merge and set explicit mergeinfo on it if it meets the aforementioned
4525 conditions. */
4526 if (notify_b->added_paths)
4528 apr_hash_index_t *hi;
4530 for (hi = apr_hash_first(NULL, notify_b->added_paths); hi;
4531 hi = apr_hash_next(hi))
4533 const void *key;
4534 const char *added_path;
4535 const svn_string_t *added_path_parent_propval;
4537 apr_hash_this(hi, &key, NULL, NULL);
4538 added_path = key;
4540 apr_pool_clear(iterpool);
4542 /* Rather than using svn_client__get_wc_mergeinfo() and
4543 analyzing the mergeinfo it returns to determine if
4544 ADDED_PATH's parent has non-inheritable mergeinfo, it is
4545 much simpler to just get the svn_string_t representation
4546 of the svn:mergeinfo prop and look for the '*'
4547 non-inheritable marker. */
4548 SVN_ERR(svn_wc_prop_get(&added_path_parent_propval,
4549 SVN_PROP_MERGEINFO,
4550 svn_path_dirname(added_path, iterpool),
4551 adm_access, iterpool));
4552 if (added_path_parent_propval
4553 && strstr(added_path_parent_propval->data,
4554 SVN_MERGEINFO_NONINHERITABLE_STR))
4556 /* ADDED_PATH's immediate parent has non-inheritable
4557 mergeinfo. */
4558 svn_boolean_t inherited;
4559 svn_merge_range_t *rng;
4560 svn_mergeinfo_t merge_mergeinfo, added_path_mergeinfo;
4561 apr_array_header_t *rangelist;
4562 const svn_wc_entry_t *entry;
4563 const char *common_ancestor_path =
4564 svn_path_get_longest_ancestor(added_path,
4565 target_merge_path->path,
4566 iterpool);
4567 const char *relative_added_path =
4568 added_path + strlen(common_ancestor_path) + 1;
4569 SVN_ERR(svn_wc__entry_versioned(&entry, added_path,
4570 adm_access, FALSE,
4571 iterpool));
4573 /* Calculate the mergeinfo resulting from this merge. */
4574 merge_mergeinfo = apr_hash_make(iterpool);
4575 rangelist = apr_array_make(iterpool, 1,
4576 sizeof(svn_merge_range_t *));
4577 rng = svn_merge_range_dup(&range, iterpool);
4578 if (entry->kind == svn_node_file)
4579 rng->inheritable = TRUE;
4580 else
4581 rng->inheritable =
4582 (!(depth == svn_depth_infinity
4583 || depth == svn_depth_immediates));
4584 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = rng;
4585 apr_hash_set(merge_mergeinfo,
4586 svn_path_join(mergeinfo_path,
4587 relative_added_path,
4588 iterpool),
4589 APR_HASH_KEY_STRING, rangelist);
4591 /* Get any explicit mergeinfo the added path has. */
4592 SVN_ERR(svn_client__get_wc_mergeinfo(
4593 &added_path_mergeinfo, &inherited, FALSE,
4594 svn_mergeinfo_explicit, entry, added_path,
4595 NULL, NULL, adm_access, merge_b->ctx, iterpool));
4597 /* Combine the explict mergeinfo on the added path (if any)
4598 with the mergeinfo for this merge. */
4599 if (added_path_mergeinfo)
4600 SVN_ERR(svn_mergeinfo_merge(merge_mergeinfo,
4601 added_path_mergeinfo,
4602 iterpool));
4603 SVN_ERR(svn_client__record_wc_mergeinfo(added_path,
4604 merge_mergeinfo,
4605 adm_access,
4606 iterpool));
4610 } /* (!merge_b->dry_run && merge_b->same_repos) */
4612 svn_pool_destroy(iterpool);
4613 return err;
4617 /* Drive a merge of MERGE_SOURCES into working copy path TARGET (with
4618 associated TARGET_ENTRY and ADM_ACCESS baton).
4620 If SOURCES_ANCESTRAL is set, then for every merge source in
4621 MERGE_SOURCES, the "left" and "right" side of the merge source are
4622 ancestrally related. (See 'MERGEINFO MERGE SOURCE NORMALIZATION'
4623 for more on what that means and how it matters.)
4625 If SOURCES_RELATED is set, the "left" and "right" sides of the
4626 merge source are historically related (ancestors, uncles, second
4627 cousins thrice removed, etc...). (This is passed through to
4628 do_file_merge() to simulate the history checks that the repository
4629 logic does in the directory case.)
4631 SAME_REPOS is TRUE iff the merge sources live in the same
4632 repository as the one from which the target working copy has been
4633 checked out.
4635 FORCE, DRY_RUN, RECORD_ONLY, IGNORE_ANCESTRY, DEPTH, MERGE_OPTIONS,
4636 and CTX are as described in the docstring for svn_client_merge_peg3().
4638 static svn_error_t *
4639 do_merge(apr_array_header_t *merge_sources,
4640 const char *target,
4641 const svn_wc_entry_t *target_entry,
4642 svn_wc_adm_access_t *adm_access,
4643 svn_boolean_t sources_ancestral,
4644 svn_boolean_t sources_related,
4645 svn_boolean_t same_repos,
4646 svn_boolean_t ignore_ancestry,
4647 svn_boolean_t force,
4648 svn_boolean_t dry_run,
4649 svn_boolean_t record_only,
4650 svn_depth_t depth,
4651 const apr_array_header_t *merge_options,
4652 svn_client_ctx_t *ctx,
4653 apr_pool_t *pool)
4655 apr_pool_t *subpool = svn_pool_create(pool);
4656 merge_cmd_baton_t merge_cmd_baton;
4657 notification_receiver_baton_t notify_baton;
4658 svn_config_t *cfg;
4659 const char *diff3_cmd;
4660 int i;
4661 svn_boolean_t checked_mergeinfo_capability = FALSE;
4663 /* If this is a dry-run record-only merge, there's nothing to do. */
4664 if (record_only && dry_run)
4665 return SVN_NO_ERROR;
4667 /* Sanity check: we can do a record-only merge (which is a
4668 merge-tracking thing) if the sources aren't related, because we
4669 don't do merge-tracking if the sources aren't related. */
4670 if (record_only && (! sources_ancestral))
4671 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
4672 _("Use of two URLs is not compatible with "
4673 "mergeinfo modification"));
4675 /* Ensure a known depth. */
4676 if (depth == svn_depth_unknown)
4677 depth = target_entry->depth;
4679 /* Set up the diff3 command, so various callers don't have to. */
4680 cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
4681 APR_HASH_KEY_STRING) : NULL;
4682 svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS,
4683 SVN_CONFIG_OPTION_DIFF3_CMD, NULL);
4686 /* Build the merge context baton (or at least the parts of it that
4687 don't need to be reset for each merge source). */
4688 merge_cmd_baton.force = force;
4689 merge_cmd_baton.dry_run = dry_run;
4690 merge_cmd_baton.record_only = record_only;
4691 merge_cmd_baton.ignore_ancestry = ignore_ancestry;
4692 merge_cmd_baton.same_repos = same_repos;
4693 merge_cmd_baton.mergeinfo_capable = FALSE;
4694 merge_cmd_baton.sources_ancestral = sources_ancestral;
4695 merge_cmd_baton.ctx = ctx;
4696 merge_cmd_baton.target_missing_child = FALSE;
4697 merge_cmd_baton.target = target;
4698 merge_cmd_baton.pool = subpool;
4699 merge_cmd_baton.merge_options = merge_options;
4700 merge_cmd_baton.diff3_cmd = diff3_cmd;
4701 merge_cmd_baton.first_range = TRUE;
4702 merge_cmd_baton.working_mergeinfo = NULL;
4703 merge_cmd_baton.long_pool = pool;
4705 /* Build the notification receiver baton. */
4706 notify_baton.wrapped_func = ctx->notify_func2;
4707 notify_baton.wrapped_baton = ctx->notify_baton2;
4708 notify_baton.nbr_notifications = 0;
4709 notify_baton.nbr_operative_notifications = 0;
4710 notify_baton.merged_paths = NULL;
4711 notify_baton.skipped_paths = NULL;
4712 notify_baton.added_paths = NULL;
4713 notify_baton.is_single_file_merge = FALSE;
4714 notify_baton.children_with_mergeinfo = NULL;
4715 notify_baton.cur_ancestor_index = -1;
4716 notify_baton.merge_b = &merge_cmd_baton;
4717 notify_baton.pool = pool;
4719 for (i = 0; i < merge_sources->nelts; i++)
4721 merge_source_t *merge_source =
4722 APR_ARRAY_IDX(merge_sources, i, merge_source_t *);
4723 const char *url1, *url2;
4724 svn_revnum_t rev1, rev2;
4725 svn_ra_session_t *ra_session1, *ra_session2;
4727 svn_pool_clear(subpool);
4729 /* Convenience variables. */
4730 url1 = merge_source->url1;
4731 url2 = merge_source->url2;
4732 rev1 = merge_source->rev1;
4733 rev2 = merge_source->rev2;
4735 /* Sanity check: if our left- and right-side merge sources are
4736 the same, there's nothing to here. */
4737 if ((strcmp(url1, url2) == 0) && (rev1 == rev2))
4738 continue;
4740 /* Establish RA sessions to our URLs. */
4741 SVN_ERR(svn_client__open_ra_session_internal(&ra_session1, url1,
4742 NULL, NULL, NULL,
4743 FALSE, TRUE, ctx, subpool));
4744 SVN_ERR(svn_client__open_ra_session_internal(&ra_session2, url2,
4745 NULL, NULL, NULL,
4746 FALSE, TRUE, ctx, subpool));
4748 /* Populate the portions of the merge context baton that need to
4749 be reset for each merge source iteration. */
4750 merge_cmd_baton.url = url2;
4751 merge_cmd_baton.added_path = NULL;
4752 merge_cmd_baton.add_necessitated_merge = FALSE;
4753 merge_cmd_baton.dry_run_deletions =
4754 dry_run ? apr_hash_make(subpool) : NULL;
4755 merge_cmd_baton.conflicted_paths = NULL;
4756 merge_cmd_baton.operative_merge = FALSE;
4757 merge_cmd_baton.target_has_dummy_merge_range = FALSE;
4758 if (i == 0)
4759 merge_cmd_baton.working_mergeinfo = apr_hash_make(pool);
4760 if (i > 0)
4761 merge_cmd_baton.first_range = FALSE;
4762 merge_cmd_baton.ra_session1 = ra_session1;
4763 merge_cmd_baton.ra_session2 = ra_session2;
4765 /* Populate the portions of the merge context baton that require
4766 an RA session to set, but shouldn't be reset for each iteration. */
4767 if (! checked_mergeinfo_capability)
4769 SVN_ERR(svn_ra_has_capability(ra_session1,
4770 &merge_cmd_baton.mergeinfo_capable,
4771 SVN_RA_CAPABILITY_MERGEINFO, subpool));
4772 checked_mergeinfo_capability = TRUE;
4775 /* If this is a record-only merge and our sources are from the
4776 same repository as our target, just do the record and move on. */
4777 if (same_repos && record_only)
4779 const char *merge_source_url = (rev1 < rev2) ? url2 : url1;
4780 svn_merge_range_t range;
4781 range.start = rev1;
4782 range.end = rev2;
4783 range.inheritable = TRUE;
4784 SVN_ERR(record_mergeinfo_for_record_only_merge(merge_source_url,
4785 &range,
4786 target_entry,
4787 adm_access,
4788 &merge_cmd_baton,
4789 subpool));
4791 continue;
4794 /* Call our merge helpers based on entry kind. */
4795 if (target_entry->kind == svn_node_file)
4797 SVN_ERR(do_file_merge(url1, rev1, url2, rev2, target,
4798 sources_related, adm_access,
4799 &notify_baton, &merge_cmd_baton, subpool));
4801 else if (target_entry->kind == svn_node_dir)
4803 SVN_ERR(do_directory_merge(url1, rev1, url2, rev2, target_entry,
4804 adm_access, depth, &notify_baton,
4805 &merge_cmd_baton, subpool));
4808 /* The final mergeinfo on TARGET_WCPATH may itself elide. */
4809 if ((! dry_run) && merge_cmd_baton.operative_merge)
4810 SVN_ERR(svn_client__elide_mergeinfo(target, NULL, target_entry,
4811 adm_access, ctx, subpool));
4814 /* If this was ultimately a no-op merge, then remove any temporary
4815 changes made by get_mergeinfo_paths() to the working mergeinfo. */
4816 if (!dry_run
4817 && !merge_cmd_baton.operative_merge
4818 && merge_cmd_baton.working_mergeinfo)
4820 apr_hash_index_t *hi;
4822 for (hi = apr_hash_first(NULL, merge_cmd_baton.working_mergeinfo);
4823 hi; hi = apr_hash_next(hi))
4825 const void *key;
4826 void *value;
4827 const char *path;
4828 working_mergeinfo_t *working_mergeinfo;
4829 svn_error_t *err;
4831 apr_hash_this(hi, &key, NULL, &value);
4832 path = key;
4833 working_mergeinfo = value;
4834 err = svn_wc_prop_set2(SVN_PROP_MERGEINFO,
4835 working_mergeinfo->working_mergeinfo_propval,
4836 path, adm_access, TRUE, pool);
4837 /* If a directory PATH was skipped because it is missing we can't
4838 restore it's working mergeinfo, so ignore it and continue. */
4839 if (err)
4841 if (err->apr_err == SVN_ERR_WC_NOT_LOCKED)
4843 svn_error_clear(err);
4844 continue;
4846 else
4848 return err;
4854 svn_pool_destroy(subpool);
4855 return SVN_NO_ERROR;
4858 /* Perform a two-URL merge between URLs which are related, but neither
4859 is a direct ancestor of the other. This first does a real two-URL
4860 merge (unless this is record-only), followed by record-only merges
4861 to represent the changed mergeinfo.
4863 The merge is between URL1@REV1 (in RA_SESSION1) and URL2@REV2 (in
4864 RA_SESSION2); YC_REV is their youngest common ancestor.
4865 SOURCE_REPOS_ROOT and WC_REPOS_ROOT are the repository roots of the
4866 source URL and the target working copy. ENTRY is the wc entry for
4867 TARGET_WCPATH. Other arguments are as in all of the public merge
4868 APIs.
4870 static svn_error_t *
4871 merge_cousins_and_supplement_mergeinfo(const char *target_wcpath,
4872 const svn_wc_entry_t *entry,
4873 svn_wc_adm_access_t *adm_access,
4874 svn_ra_session_t *ra_session,
4875 const char *URL1,
4876 svn_revnum_t rev1,
4877 const char *URL2,
4878 svn_revnum_t rev2,
4879 svn_revnum_t yc_rev,
4880 const char *source_repos_root,
4881 const char *wc_repos_root,
4882 svn_depth_t depth,
4883 svn_boolean_t ignore_ancestry,
4884 svn_boolean_t force,
4885 svn_boolean_t record_only,
4886 svn_boolean_t dry_run,
4887 const apr_array_header_t *merge_options,
4888 svn_client_ctx_t *ctx,
4889 apr_pool_t *pool)
4891 svn_opt_revision_range_t *range;
4892 apr_array_header_t *remove_sources, *add_sources, *ranges;
4893 svn_opt_revision_t peg_revision;
4894 const char *old_url;
4896 peg_revision.kind = svn_opt_revision_number;
4897 SVN_ERR(svn_ra_get_session_url(ra_session, &old_url, pool));
4899 range = apr_pcalloc(pool, sizeof(*range));
4900 range->start.kind = svn_opt_revision_number;
4901 range->start.value.number = rev1;
4902 range->end.kind = svn_opt_revision_number;
4903 range->end.value.number = yc_rev;
4904 ranges = apr_array_make(pool, 2, sizeof(svn_opt_revision_range_t *));
4905 APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
4906 peg_revision.value.number = rev1;
4907 SVN_ERR(svn_ra_reparent(ra_session, URL1, pool));
4908 SVN_ERR(normalize_merge_sources(&remove_sources, URL1, URL1,
4909 source_repos_root, &peg_revision,
4910 ranges, ra_session, ctx, pool));
4912 range = apr_pcalloc(pool, sizeof(*range));
4913 range->start.kind = svn_opt_revision_number;
4914 range->start.value.number = yc_rev;
4915 range->end.kind = svn_opt_revision_number;
4916 range->end.value.number = rev2;
4917 ranges = apr_array_make(pool, 2, sizeof(svn_opt_revision_range_t *));
4918 APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
4919 peg_revision.value.number = rev2;
4920 SVN_ERR(svn_ra_reparent(ra_session, URL2, pool));
4921 SVN_ERR(normalize_merge_sources(&add_sources, URL2, URL2,
4922 source_repos_root, &peg_revision,
4923 ranges, ra_session, ctx, pool));
4925 SVN_ERR(svn_ra_reparent(ra_session, old_url, pool));
4927 /* If this isn't a record-only merge, we'll first do a stupid
4928 point-to-point merge... */
4929 if (! record_only)
4931 merge_source_t *faux_source;
4932 apr_array_header_t *faux_sources =
4933 apr_array_make(pool, 1, sizeof(merge_source_t *));
4934 faux_source = apr_pcalloc(pool, sizeof(*faux_source));
4935 faux_source->url1 = URL1;
4936 faux_source->url2 = URL2;
4937 faux_source->rev1 = rev1;
4938 faux_source->rev2 = rev2;
4939 APR_ARRAY_PUSH(faux_sources, merge_source_t *) = faux_source;
4940 SVN_ERR(do_merge(faux_sources, target_wcpath, entry, adm_access,
4941 FALSE, TRUE,
4942 (strcmp(wc_repos_root, source_repos_root) == 0),
4943 ignore_ancestry, force, dry_run,
4944 FALSE, depth, merge_options, ctx, pool));
4946 /* ... and now we do a pair of record-only merges using the real
4947 sources we've calculated. (We know that each tong in our
4948 fork of our merge source history tree has an ancestral
4949 relationship with the common ancestral, so we force
4950 ancestral=TRUE here.) */
4951 SVN_ERR(do_merge(add_sources, target_wcpath, entry,
4952 adm_access, TRUE, TRUE,
4953 (strcmp(wc_repos_root, source_repos_root) == 0),
4954 ignore_ancestry, force, dry_run,
4955 TRUE, depth, merge_options, ctx, pool));
4956 SVN_ERR(do_merge(remove_sources, target_wcpath, entry,
4957 adm_access, TRUE, TRUE,
4958 (strcmp(wc_repos_root, source_repos_root) == 0),
4959 ignore_ancestry, force, dry_run,
4960 TRUE, depth, merge_options, ctx, pool));
4962 return SVN_NO_ERROR;
4965 /*-----------------------------------------------------------------------*/
4967 /*** Public APIs ***/
4969 svn_error_t *
4970 svn_client_merge3(const char *source1,
4971 const svn_opt_revision_t *revision1,
4972 const char *source2,
4973 const svn_opt_revision_t *revision2,
4974 const char *target_wcpath,
4975 svn_depth_t depth,
4976 svn_boolean_t ignore_ancestry,
4977 svn_boolean_t force,
4978 svn_boolean_t record_only,
4979 svn_boolean_t dry_run,
4980 const apr_array_header_t *merge_options,
4981 svn_client_ctx_t *ctx,
4982 apr_pool_t *pool)
4984 svn_wc_adm_access_t *adm_access;
4985 const svn_wc_entry_t *entry;
4986 const char *URL1, *URL2;
4987 svn_revnum_t rev1, rev2;
4988 svn_boolean_t related = FALSE, ancestral = FALSE;
4989 const char *wc_repos_root, *source_repos_root;
4990 svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
4991 svn_ra_session_t *ra_session1, *ra_session2;
4992 apr_array_header_t *merge_sources;
4993 merge_source_t *merge_source;
4994 svn_opt_revision_t working_rev;
4995 const char *yc_path = NULL;
4996 svn_revnum_t yc_rev = SVN_INVALID_REVNUM;
4997 apr_pool_t *sesspool;
4998 svn_boolean_t same_repos;
5000 /* Sanity check our input -- we require specified revisions. */
5001 if ((revision1->kind == svn_opt_revision_unspecified)
5002 || (revision2->kind == svn_opt_revision_unspecified))
5003 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
5004 _("Not all required revisions are specified"));
5006 /* ### FIXME: This function really ought to do a history check on
5007 the left and right sides of the merge source, and -- if one is an
5008 ancestor of the other -- just call svn_client_merge_peg3() with
5009 the appropriate args. */
5011 /* If source1 or source2 are paths, we need to get the underlying
5012 URL from the wc and save the initial path we were passed so we
5013 can use it as a path parameter (either in the baton or not).
5014 otherwise, the path will just be NULL, which means we won't be
5015 able to figure out some kind of revision specifications, but in
5016 that case it won't matter, because those ways of specifying a
5017 revision are meaningless for a url. */
5018 SVN_ERR(svn_client_url_from_path(&URL1, source1, pool));
5019 if (! URL1)
5020 return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
5021 _("'%s' has no URL"),
5022 svn_path_local_style(source1, pool));
5024 SVN_ERR(svn_client_url_from_path(&URL2, source2, pool));
5025 if (! URL2)
5026 return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
5027 _("'%s' has no URL"),
5028 svn_path_local_style(source2, pool));
5030 /* Open an admistrative session with the working copy. */
5031 SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, target_wcpath,
5032 ! dry_run, -1, ctx->cancel_func,
5033 ctx->cancel_baton, pool));
5035 /* Fetch the target's entry. */
5036 SVN_ERR(svn_wc__entry_versioned(&entry, target_wcpath, adm_access,
5037 FALSE, pool));
5039 /* Determine the working copy target's repository root URL. */
5040 working_rev.kind = svn_opt_revision_working;
5041 SVN_ERR(svn_client__get_repos_root(&wc_repos_root, target_wcpath,
5042 &working_rev, adm_access, ctx, pool));
5044 /* Open some RA sessions to our merge source sides. */
5045 sesspool = svn_pool_create(pool);
5046 SVN_ERR(svn_client__open_ra_session_internal(&ra_session1,
5047 URL1, NULL, NULL, NULL,
5048 FALSE, TRUE, ctx, sesspool));
5049 SVN_ERR(svn_client__open_ra_session_internal(&ra_session2,
5050 URL2, NULL, NULL, NULL,
5051 FALSE, TRUE, ctx, sesspool));
5053 /* Resolve revisions to real numbers. */
5054 SVN_ERR(svn_client__get_revision_number(&rev1, &youngest_rev, ra_session1,
5055 revision1, NULL, sesspool));
5056 SVN_ERR(svn_client__get_revision_number(&rev2, &youngest_rev, ra_session2,
5057 revision2, NULL, sesspool));
5059 /* Get the repository root URL from one of our sessions (the other
5060 doesn't matter -- if it ain't the same, other stuff would fall
5061 over later). */
5062 SVN_ERR(svn_ra_get_repos_root2(ra_session1, &source_repos_root, sesspool));
5064 /* Do our working copy and sources come from the same repository? */
5065 same_repos = (strcmp(source_repos_root, wc_repos_root) == 0) ? TRUE : FALSE;
5067 /* Unless we're ignoring ancestry, see if the two sources are related. */
5068 if (! ignore_ancestry)
5069 SVN_ERR(svn_client__get_youngest_common_ancestor(&yc_path, &yc_rev,
5070 URL1, rev1,
5071 URL2, rev2,
5072 ctx, pool));
5074 /* Check for a youngest common ancestor. If we have one, we'll be
5075 doing merge tracking.
5077 So, given a requested merge of the differences between A and
5078 B, and a common ancestor of C, we will find ourselves in one of
5079 four positions, and four different approaches:
5081 A == B == C there's nothing to merge
5083 A == C != B we merge the changes between A (or C) and B
5085 B == C != A we merge the changes between B (or C) and A
5087 A != B != C we merge the changes between A and B without
5088 merge recording, then record-only two merges:
5089 from A to C, and from C to B
5091 if (yc_path && SVN_IS_VALID_REVNUM(yc_rev))
5093 apr_array_header_t *ranges;
5094 svn_opt_revision_range_t *range;
5095 svn_opt_revision_t peg_revision;
5096 peg_revision.kind = svn_opt_revision_number;
5098 /* Note that our merge sources are related. */
5099 related = TRUE;
5101 /* Make YC_PATH into a full URL. */
5102 yc_path = svn_path_join(source_repos_root,
5103 svn_path_uri_encode(yc_path, pool), pool);
5105 /* If the common ancestor matches the right side of our merge,
5106 then we only need to reverse-merge the left side. */
5107 if ((strcmp(yc_path, URL2) == 0) && (yc_rev == rev2))
5109 ancestral = TRUE;
5110 range = apr_pcalloc(pool, sizeof(*range));
5111 range->start.kind = svn_opt_revision_number;
5112 range->start.value.number = rev1;
5113 range->end.kind = svn_opt_revision_number;
5114 range->end.value.number = yc_rev;
5115 ranges = apr_array_make(pool, 2, sizeof(svn_opt_revision_range_t *));
5116 APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
5117 peg_revision.value.number = rev1;
5118 SVN_ERR(normalize_merge_sources(&merge_sources, URL1, URL1,
5119 source_repos_root, &peg_revision,
5120 ranges, ra_session1, ctx, pool));
5122 /* If the common ancestor matches the left side of our merge,
5123 then we only need to merge the right side. */
5124 else if ((strcmp(yc_path, URL1) == 0) && (yc_rev == rev1))
5126 ancestral = TRUE;
5127 range = apr_pcalloc(pool, sizeof(*range));
5128 range->start.kind = svn_opt_revision_number;
5129 range->start.value.number = yc_rev;
5130 range->end.kind = svn_opt_revision_number;
5131 range->end.value.number = rev2;
5132 ranges = apr_array_make(pool, 2, sizeof(svn_opt_revision_range_t *));
5133 APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
5134 peg_revision.value.number = rev2;
5135 SVN_ERR(normalize_merge_sources(&merge_sources, URL2, URL2,
5136 source_repos_root, &peg_revision,
5137 ranges, ra_session2, ctx, pool));
5139 /* And otherwise, we need to do both: reverse merge the left
5140 side, and merge the right. */
5141 else
5143 SVN_ERR(merge_cousins_and_supplement_mergeinfo(target_wcpath, entry,
5144 adm_access,
5145 ra_session1,
5146 URL1, rev1,
5147 URL2, rev2,
5148 yc_rev,
5149 source_repos_root,
5150 wc_repos_root,
5151 depth,
5152 ignore_ancestry, force,
5153 record_only, dry_run,
5154 merge_options, ctx,
5155 pool));
5157 /* Close our temporary RA sessions (this could've happened
5158 after the second call to normalize_merge_sources() inside
5159 the merge_cousins_and_supplement_mergeinfo() routine). */
5160 svn_pool_destroy(sesspool);
5162 SVN_ERR(svn_wc_adm_close(adm_access));
5163 return SVN_NO_ERROR;
5166 else
5168 /* Build a single-item merge_source_t array. */
5169 merge_sources = apr_array_make(pool, 1, sizeof(merge_source_t *));
5170 merge_source = apr_pcalloc(pool, sizeof(*merge_source));
5171 merge_source->url1 = URL1;
5172 merge_source->url2 = URL2;
5173 merge_source->rev1 = rev1;
5174 merge_source->rev2 = rev2;
5175 APR_ARRAY_PUSH(merge_sources, merge_source_t *) = merge_source;
5178 /* Close our temporary RA sessions. */
5179 svn_pool_destroy(sesspool);
5181 SVN_ERR(do_merge(merge_sources, target_wcpath, entry, adm_access,
5182 ancestral, related, same_repos,
5183 ignore_ancestry, force, dry_run,
5184 record_only, depth, merge_options, ctx, pool));
5186 SVN_ERR(svn_wc_adm_close(adm_access));
5188 return SVN_NO_ERROR;
5191 svn_error_t *
5192 svn_client_merge2(const char *source1,
5193 const svn_opt_revision_t *revision1,
5194 const char *source2,
5195 const svn_opt_revision_t *revision2,
5196 const char *target_wcpath,
5197 svn_boolean_t recurse,
5198 svn_boolean_t ignore_ancestry,
5199 svn_boolean_t force,
5200 svn_boolean_t dry_run,
5201 const apr_array_header_t *merge_options,
5202 svn_client_ctx_t *ctx,
5203 apr_pool_t *pool)
5205 return svn_client_merge3(source1, revision1, source2, revision2,
5206 target_wcpath,
5207 SVN_DEPTH_INFINITY_OR_FILES(recurse),
5208 ignore_ancestry, force, FALSE, dry_run,
5209 merge_options, ctx, pool);
5212 svn_error_t *
5213 svn_client_merge(const char *source1,
5214 const svn_opt_revision_t *revision1,
5215 const char *source2,
5216 const svn_opt_revision_t *revision2,
5217 const char *target_wcpath,
5218 svn_boolean_t recurse,
5219 svn_boolean_t ignore_ancestry,
5220 svn_boolean_t force,
5221 svn_boolean_t dry_run,
5222 svn_client_ctx_t *ctx,
5223 apr_pool_t *pool)
5225 return svn_client_merge2(source1, revision1, source2, revision2,
5226 target_wcpath, recurse, ignore_ancestry, force,
5227 dry_run, NULL, ctx, pool);
5231 /* If TARGET_WCPATH does not reflect a single-revision,
5232 svn_depth_infinity, pristine, unswitched working copy -- in other
5233 words, a subtree found in a single revision -- raise
5234 SVN_ERR_CLIENT_NOT_READY_TO_MERGE. */
5235 static svn_error_t *
5236 ensure_wc_reflects_repository_subtree(const char *target_wcpath,
5237 svn_client_ctx_t *ctx,
5238 apr_pool_t *pool)
5240 svn_wc_revision_status_t *wc_stat;
5242 /* Get a WC summary with min/max revisions set to the BASE revision. */
5243 SVN_ERR(svn_wc_revision_status(&wc_stat, target_wcpath, NULL, FALSE,
5244 ctx->cancel_func, ctx->cancel_baton, pool));
5246 if (wc_stat->switched)
5247 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
5248 _("Cannot reintegrate into a working copy "
5249 "with a switched subtree"));
5251 if (wc_stat->sparse_checkout)
5252 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
5253 _("Cannot reintegrate into a working copy "
5254 "not entirely at infinite depth"));
5256 if (wc_stat->modified)
5257 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
5258 _("Cannot reintegrate into a working copy "
5259 "that has local modifications"));
5261 if (! (SVN_IS_VALID_REVNUM(wc_stat->min_rev)
5262 && SVN_IS_VALID_REVNUM(wc_stat->max_rev)))
5263 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
5264 _("Cannot determine revision of working copy"));
5266 if (wc_stat->min_rev != wc_stat->max_rev)
5267 return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
5268 _("Cannot reintegrate into mixed-revision "
5269 "working copy; try updating first"));
5271 return SVN_NO_ERROR;
5274 /* Given a "mergeinfo" hash HISTORY_AS_MERGEINFO representing revision
5275 ranges from a merge target that are not represented in the merge
5276 source, check (using RA_SESSION, which is pointed at the repository
5277 root) that all of the ranges in the hash are "phantoms": that is,
5278 their corresponding path did not change in any of their revisions.
5279 Raises SVN_ERR_CLIENT_NOT_READY_TO_MERGE if any are not phantoms.
5280 Temporary allocations in POOL.
5282 static svn_error_t *
5283 ensure_all_missing_ranges_are_phantoms(svn_ra_session_t *ra_session,
5284 svn_mergeinfo_t history_as_mergeinfo,
5285 apr_pool_t *pool)
5287 apr_hash_index_t *hi;
5288 apr_pool_t *iterpool = svn_pool_create(pool);
5290 for (hi = apr_hash_first(pool, history_as_mergeinfo); hi;
5291 hi = apr_hash_next(hi))
5293 const void *key;
5294 void *value;
5295 const char *path;
5296 apr_array_header_t *rangelist;
5297 int i;
5299 apr_hash_this(hi, &key, NULL, &value);
5300 path = key;
5301 rangelist = value;
5303 /* mergeinfo hashes contain paths that start with slashes;
5304 ra APIs take paths without slashes. */
5305 assert(*path);
5306 path++;
5308 for (i = 0; i < rangelist->nelts; i++)
5310 svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
5311 svn_merge_range_t *);
5312 svn_dirent_t *dirent;
5314 /* This function should not receive any "rollback"
5315 ranges. */
5316 assert(range->start < range->end);
5318 svn_pool_clear(iterpool);
5320 SVN_ERR(svn_ra_stat(ra_session,
5321 path,
5322 range->end,
5323 &dirent,
5324 iterpool));
5326 if (svn_merge_range_contains_rev(range, dirent->created_rev))
5328 const char *full_url;
5330 svn_pool_destroy(iterpool);
5332 SVN_ERR(svn_ra_get_session_url(ra_session, &full_url, pool));
5333 full_url = svn_path_url_add_component(full_url, path, pool);
5334 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
5335 _("At least one revision (r%ld) "
5336 "not yet merged from '%s'"),
5337 dirent->created_rev, full_url);
5342 svn_pool_destroy(iterpool);
5343 return SVN_NO_ERROR;
5347 /* Return a new catalog in *CATALOG_P equal to CATALOG, but containing
5348 only source ranges from the segments in SEGMENTS (whose elements
5349 are of type svn_location_segment_t *). The returned values are
5350 (deeply) allocated in POOL. */
5351 static svn_error_t *
5352 remove_irrelevant_ranges(svn_mergeinfo_catalog_t *catalog_p,
5353 svn_mergeinfo_catalog_t catalog,
5354 apr_array_header_t *segments,
5355 apr_pool_t *pool)
5357 apr_hash_index_t *hi;
5358 svn_mergeinfo_t new_by_path = apr_hash_make(pool);
5359 svn_mergeinfo_t history_as_mergeinfo;
5361 SVN_ERR(svn_client__mergeinfo_from_segments(&history_as_mergeinfo,
5362 segments,
5363 pool));
5365 for (hi = apr_hash_first(pool, catalog);
5367 hi = apr_hash_next(hi))
5369 const void *key;
5370 void *val;
5371 const char *path;
5372 svn_mergeinfo_t mergeinfo, filtered_mergeinfo;
5374 apr_hash_this(hi, &key, NULL, &val);
5375 path = key;
5376 mergeinfo = val;
5378 SVN_ERR(svn_mergeinfo_intersect(&filtered_mergeinfo,
5379 mergeinfo,
5380 history_as_mergeinfo,
5381 pool));
5382 if (filtered_mergeinfo
5383 && apr_hash_count(filtered_mergeinfo) > 0)
5384 apr_hash_set(new_by_path, path, APR_HASH_KEY_STRING,
5385 filtered_mergeinfo);
5388 *catalog_p = new_by_path;
5389 return SVN_NO_ERROR;
5392 /* TODO(reint): Document.
5394 RA_SESSION must be opened at the repository root. */
5395 static svn_error_t *
5396 calculate_left_hand_side(const char **url_left,
5397 svn_revnum_t *rev_left,
5398 svn_mergeinfo_t *source_mergeinfo_p,
5399 const char *target_repos_rel_path,
5400 svn_revnum_t target_rev,
5401 const char *source_repos_rel_path,
5402 const char *source_repos_root,
5403 svn_revnum_t source_rev,
5404 svn_ra_session_t *ra_session,
5405 svn_client_ctx_t *ctx,
5406 apr_pool_t *pool)
5408 apr_array_header_t *segments; /* array of (svn_location_segment_t *) */
5409 svn_boolean_t have_mergeinfo_for_source = FALSE,
5410 have_mergeinfo_for_descendants = FALSE;
5411 svn_mergeinfo_catalog_t mergeinfo_catalog;
5412 apr_array_header_t *source_repos_rel_path_as_array
5413 = apr_array_make(pool, 1, sizeof(const char *));
5414 apr_pool_t *subpool = svn_pool_create(pool);
5416 /* Get the history (segments) for the target */
5417 SVN_ERR(svn_client__repos_location_segments(&segments,
5418 ra_session,
5419 target_repos_rel_path,
5420 target_rev, target_rev,
5421 SVN_INVALID_REVNUM,
5422 ctx, subpool));
5424 /* Get the mergeinfo from the source, including its descendants. */
5425 APR_ARRAY_PUSH(source_repos_rel_path_as_array, const char *)
5426 = source_repos_rel_path;
5427 SVN_ERR(svn_ra_get_mergeinfo(ra_session, &mergeinfo_catalog,
5428 source_repos_rel_path_as_array, source_rev,
5429 svn_mergeinfo_inherited, TRUE, subpool));
5430 if (!mergeinfo_catalog)
5431 mergeinfo_catalog = apr_hash_make(subpool);
5433 /* Filter mergeinfo_catalog so that all of the ranges come from
5434 the target's history */
5435 SVN_ERR(remove_irrelevant_ranges(&mergeinfo_catalog,
5436 mergeinfo_catalog,
5437 segments,
5438 subpool));
5440 /* Elide! */
5441 SVN_ERR(svn_client__elide_mergeinfo_catalog(mergeinfo_catalog, subpool));
5443 /* See which case we fall into: */
5444 /* TODO(reint): make sure we look things up with keys that start
5445 with slash. This may not be as simple as it sounds, since
5446 source_repos_rel_path is also used as the component argument to
5447 (e.g.) svn_path_join(), which expects the component to *not*
5448 start with a slash (or at least, it will behave in a way we
5449 probably don't want if the component *does* start with slash).
5451 if (apr_hash_get(mergeinfo_catalog, source_repos_rel_path,
5452 APR_HASH_KEY_STRING))
5453 have_mergeinfo_for_source = TRUE;
5454 if (apr_hash_count(mergeinfo_catalog) > 1 ||
5455 (! have_mergeinfo_for_source && apr_hash_count(mergeinfo_catalog) == 1))
5456 have_mergeinfo_for_descendants = TRUE;
5458 if (! have_mergeinfo_for_source && ! have_mergeinfo_for_descendants)
5460 /* TODO(reint): Make sure we're not fetching location segments
5461 over and over. */
5462 /* We never merged to the source. Just return the branch point. */
5463 const char *yc_ancestor_path,
5464 *source_url = svn_path_join(source_repos_root, source_repos_rel_path,
5465 subpool),
5466 *target_url = svn_path_join(source_repos_root, target_repos_rel_path,
5467 subpool);
5469 SVN_ERR(svn_client__get_youngest_common_ancestor(&yc_ancestor_path,
5470 rev_left,
5471 source_url, source_rev,
5472 target_url, target_rev,
5473 ctx, subpool));
5474 if (!(yc_ancestor_path && SVN_IS_VALID_REVNUM(*rev_left)))
5475 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
5476 _("'%s@%ld' must be ancestrally related to "
5477 "'%s@%ld'"), source_url, source_rev,
5478 target_url, target_rev);
5479 *url_left = svn_path_join(source_repos_root, yc_ancestor_path, pool);
5480 *source_mergeinfo_p = apr_hash_make(pool);
5482 svn_pool_destroy(subpool);
5483 return SVN_NO_ERROR;
5485 else if (! have_mergeinfo_for_descendants)
5487 /* Easy case: return the last path/rev in the mergeinfo. */
5488 svn_mergeinfo_t source_mergeinfo = apr_hash_get(mergeinfo_catalog,
5489 source_repos_rel_path,
5490 APR_HASH_KEY_STRING);
5491 apr_pool_t *iterpool = svn_pool_create(subpool);
5492 int i;
5493 for (i = segments->nelts - 1; i >= 0; i--)
5495 svn_location_segment_t *segment
5496 = APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
5497 apr_array_header_t *rangelist;
5499 svn_pool_clear(iterpool);
5501 /* Ignore gaps in history. */
5502 if (!segment->path)
5503 continue;
5505 rangelist = apr_hash_get(source_mergeinfo,
5506 apr_pstrcat(iterpool, "/", segment->path,
5507 NULL),
5508 APR_HASH_KEY_STRING);
5509 if (rangelist != NULL && rangelist->nelts > 0)
5511 svn_merge_range_t *last_range
5512 = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
5513 svn_merge_range_t *);
5514 *rev_left = last_range->end;
5515 *url_left = svn_path_join(source_repos_root, segment->path,
5516 pool);
5517 *source_mergeinfo_p = svn_mergeinfo_dup(source_mergeinfo, pool);
5518 svn_pool_destroy(iterpool);
5519 svn_pool_destroy(subpool);
5520 return SVN_NO_ERROR;
5523 /* We only got here because we had mergeinfo for the source; if
5524 there were no segments, then our logic was wrong. */
5525 abort();
5527 else
5529 const char *full_url;
5530 SVN_ERR(svn_ra_get_session_url(ra_session, &full_url, pool));
5531 full_url = svn_path_url_add_component(full_url, source_repos_rel_path,
5532 pool);
5533 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
5534 "Cannot reintegrate from '%s' yet:\n"
5535 "Some revisions have been merged under it "
5536 "that have not been merged\n"
5537 "into the reintegration target; "
5538 "merge them first, then retry.", full_url);
5539 /* TODO(reint): It would be even better to print out mergeinfo_catalog
5540 here. Is there a helper function for that? */
5542 return SVN_NO_ERROR;
5546 svn_error_t *
5547 svn_client_merge_reintegrate(const char *source,
5548 const svn_opt_revision_t *peg_revision,
5549 const char *target_wcpath,
5550 svn_boolean_t force,
5551 svn_boolean_t dry_run,
5552 const apr_array_header_t *merge_options,
5553 svn_client_ctx_t *ctx,
5554 apr_pool_t *pool)
5556 svn_wc_adm_access_t *adm_access;
5557 const svn_wc_entry_t *entry;
5558 const char *wc_repos_root, *source_repos_root;
5559 svn_opt_revision_t working_revision;
5560 svn_ra_session_t *ra_session;
5561 const char *source_repos_rel_path, *target_repos_rel_path;
5562 const char *yc_ancestor_path;
5563 svn_revnum_t yc_ancestor_rev;
5564 const char *url1, *url2;
5565 svn_revnum_t rev1, rev2;
5566 svn_mergeinfo_t source_mergeinfo;
5569 /* Open an admistrative session with the working copy. */
5570 SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, target_wcpath,
5571 (! dry_run), -1, ctx->cancel_func,
5572 ctx->cancel_baton, pool));
5574 /* Fetch the target's entry. */
5575 SVN_ERR(svn_wc__entry_versioned(&entry, target_wcpath, adm_access,
5576 FALSE, pool));
5578 /* Make sure we're dealing with a real URL. */
5579 SVN_ERR(svn_client_url_from_path(&url2, source, pool));
5580 if (! url2)
5581 return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
5582 _("'%s' has no URL"),
5583 svn_path_local_style(source, pool));
5585 /* Determine the working copy target's repository root URL. */
5586 working_revision.kind = svn_opt_revision_working;
5587 SVN_ERR(svn_client__get_repos_root(&wc_repos_root, target_wcpath,
5588 &working_revision, adm_access, ctx, pool));
5590 /* Open an RA session to our source URL, and determine its root URL. */
5591 SVN_ERR(svn_client__open_ra_session_internal(&ra_session, wc_repos_root,
5592 NULL, NULL, NULL,
5593 FALSE, FALSE, ctx, pool));
5594 SVN_ERR(svn_ra_get_repos_root2(ra_session, &source_repos_root, pool));
5596 /* source_repos_root and wc_repos_root are required to be the same,
5597 as mergeinfo doesn't come into play for cross-repository merging. */
5598 if (strcmp(source_repos_root, wc_repos_root) != 0)
5599 return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
5600 _("'%s' must be from the same repository as "
5601 "'%s'"), svn_path_local_style(source, pool),
5602 svn_path_local_style(target_wcpath, pool));
5604 SVN_ERR(ensure_wc_reflects_repository_subtree(target_wcpath, ctx, pool));
5606 /* As the WC tree is "pure", use its last-updated-to revision as
5607 the default revision for the left side of our merge, since that's
5608 what the repository sub-tree is required to be up to date with
5609 (with regard to the WC). */
5610 rev1 = entry->revision;
5612 SVN_ERR(svn_client__path_relative_to_root(&source_repos_rel_path,
5613 url2, NULL, FALSE,
5614 ra_session, NULL, pool));
5615 SVN_ERR(svn_client__path_relative_to_root(&target_repos_rel_path,
5616 target_wcpath, wc_repos_root,
5617 FALSE, ra_session, NULL, pool));
5619 SVN_ERR(svn_client__get_revision_number(&rev2, NULL,
5620 ra_session, peg_revision,
5621 source_repos_rel_path, pool));
5623 SVN_ERR(calculate_left_hand_side(&url1, &rev1, &source_mergeinfo,
5624 target_repos_rel_path,
5625 rev1,
5626 source_repos_rel_path,
5627 source_repos_root,
5628 rev2,
5629 ra_session,
5630 ctx,
5631 pool));
5633 SVN_ERR(svn_client__get_youngest_common_ancestor(&yc_ancestor_path,
5634 &yc_ancestor_rev,
5635 url2, rev2,
5636 url1, rev1,
5637 ctx, pool));
5639 if (!(yc_ancestor_path && SVN_IS_VALID_REVNUM(yc_ancestor_rev)))
5640 return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
5641 _("'%s@%ld' must be ancestrally related to "
5642 "'%s@%ld'"), url1, rev1, url2, rev2);
5644 if (rev1 > yc_ancestor_rev)
5646 /* Have we actually merged anything to the source from the
5647 target? If so, make sure we've merged a contiguous
5648 prefix. */
5649 svn_opt_revision_t opt_rev1;
5650 svn_mergeinfo_t target_mergeinfo, deleted_mergeinfo, added_mergeinfo;
5652 opt_rev1.kind = svn_opt_revision_number;
5653 opt_rev1.value.number = rev1;
5654 SVN_ERR(svn_client__get_history_as_mergeinfo(&target_mergeinfo,
5655 entry->url,
5656 &opt_rev1,
5657 rev1,
5658 yc_ancestor_rev + 1,
5659 NULL, adm_access, ctx,
5660 pool));
5662 /* ### TODO(reint): Consider CONSIDER_INHERITANCE parameter... */
5663 SVN_ERR(svn_mergeinfo_diff(&deleted_mergeinfo, &added_mergeinfo,
5664 target_mergeinfo, source_mergeinfo, FALSE,
5665 pool));
5667 SVN_ERR(ensure_all_missing_ranges_are_phantoms(ra_session,
5668 deleted_mergeinfo, pool));
5671 /* Left side: trunk@youngest-trunk-rev-merged-to-branch-at-specified-peg-rev
5672 * Right side: branch@specified-peg-revision */
5674 /* Do the real merge! */
5675 /* ### TODO(reint): Make sure that one isn't the same line ancestor
5676 ### of the other (what's erroneously referred to as "ancestrally
5677 ### related" in this source file). We can merge to trunk without
5678 ### implementing this. */
5679 SVN_ERR(merge_cousins_and_supplement_mergeinfo(target_wcpath, entry,
5680 adm_access, ra_session,
5681 url1, rev1, url2, rev2,
5682 yc_ancestor_rev,
5683 source_repos_root,
5684 wc_repos_root,
5685 svn_depth_infinity,
5686 FALSE,
5687 force, FALSE, dry_run,
5688 merge_options, ctx, pool));
5690 /* Shutdown the administrative session. */
5691 SVN_ERR(svn_wc_adm_close(adm_access));
5693 return SVN_NO_ERROR;
5698 svn_error_t *
5699 svn_client_merge_peg3(const char *source,
5700 const apr_array_header_t *ranges_to_merge,
5701 const svn_opt_revision_t *peg_revision,
5702 const char *target_wcpath,
5703 svn_depth_t depth,
5704 svn_boolean_t ignore_ancestry,
5705 svn_boolean_t force,
5706 svn_boolean_t record_only,
5707 svn_boolean_t dry_run,
5708 const apr_array_header_t *merge_options,
5709 svn_client_ctx_t *ctx,
5710 apr_pool_t *pool)
5712 svn_wc_adm_access_t *adm_access;
5713 const svn_wc_entry_t *entry;
5714 const char *URL;
5715 apr_array_header_t *merge_sources;
5716 const char *wc_repos_root, *source_repos_root;
5717 svn_opt_revision_t working_rev;
5718 svn_ra_session_t *ra_session;
5719 apr_pool_t *sesspool;
5721 /* No ranges to merge? No problem. */
5722 if (ranges_to_merge->nelts == 0)
5723 return SVN_NO_ERROR;
5725 /* Open an admistrative session with the working copy. */
5726 SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, target_wcpath,
5727 (! dry_run), -1, ctx->cancel_func,
5728 ctx->cancel_baton, pool));
5730 /* Fetch the target's entry. */
5731 SVN_ERR(svn_wc__entry_versioned(&entry, target_wcpath, adm_access,
5732 FALSE, pool));
5734 /* Make sure we're dealing with a real URL. */
5735 SVN_ERR(svn_client_url_from_path(&URL, source, pool));
5736 if (! URL)
5737 return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
5738 _("'%s' has no URL"),
5739 svn_path_local_style(source, pool));
5741 /* Determine the working copy target's repository root URL. */
5742 working_rev.kind = svn_opt_revision_working;
5743 SVN_ERR(svn_client__get_repos_root(&wc_repos_root, target_wcpath,
5744 &working_rev, adm_access, ctx, pool));
5746 /* Open an RA session to our source URL, and determine its root URL. */
5747 sesspool = svn_pool_create(pool);
5748 SVN_ERR(svn_client__open_ra_session_internal(&ra_session,
5749 URL, NULL, NULL, NULL,
5750 FALSE, TRUE, ctx, sesspool));
5751 SVN_ERR(svn_ra_get_repos_root2(ra_session, &source_repos_root, pool));
5753 /* Normalize our merge sources. */
5754 SVN_ERR(normalize_merge_sources(&merge_sources, source, URL,
5755 source_repos_root, peg_revision,
5756 ranges_to_merge, ra_session, ctx, pool));
5758 /* We're done with our little RA session. */
5759 svn_pool_destroy(sesspool);
5761 /* Do the real merge! (We say with confidence that our merge
5762 sources are both ancestral and related.) */
5763 SVN_ERR(do_merge(merge_sources, target_wcpath, entry, adm_access,
5764 TRUE, TRUE,
5765 (strcmp(wc_repos_root, source_repos_root) == 0),
5766 ignore_ancestry, force, dry_run, record_only, depth,
5767 merge_options, ctx, pool));
5769 /* Shutdown the administrative session. */
5770 SVN_ERR(svn_wc_adm_close(adm_access));
5772 return SVN_NO_ERROR;
5776 svn_error_t *
5777 svn_client_merge_peg2(const char *source,
5778 const svn_opt_revision_t *revision1,
5779 const svn_opt_revision_t *revision2,
5780 const svn_opt_revision_t *peg_revision,
5781 const char *target_wcpath,
5782 svn_boolean_t recurse,
5783 svn_boolean_t ignore_ancestry,
5784 svn_boolean_t force,
5785 svn_boolean_t dry_run,
5786 const apr_array_header_t *merge_options,
5787 svn_client_ctx_t *ctx,
5788 apr_pool_t *pool)
5790 svn_opt_revision_range_t range;
5791 apr_array_header_t *ranges_to_merge =
5792 apr_array_make(pool, 1, sizeof(svn_opt_revision_range_t *));
5794 range.start = *revision1;
5795 range.end = *revision2;
5796 APR_ARRAY_PUSH(ranges_to_merge, svn_opt_revision_range_t *) = &range;
5797 return svn_client_merge_peg3(source, ranges_to_merge,
5798 peg_revision,
5799 target_wcpath,
5800 SVN_DEPTH_INFINITY_OR_FILES(recurse),
5801 ignore_ancestry, force, FALSE, dry_run,
5802 merge_options, ctx, pool);
5805 svn_error_t *
5806 svn_client_merge_peg(const char *source,
5807 const svn_opt_revision_t *revision1,
5808 const svn_opt_revision_t *revision2,
5809 const svn_opt_revision_t *peg_revision,
5810 const char *target_wcpath,
5811 svn_boolean_t recurse,
5812 svn_boolean_t ignore_ancestry,
5813 svn_boolean_t force,
5814 svn_boolean_t dry_run,
5815 svn_client_ctx_t *ctx,
5816 apr_pool_t *pool)
5818 return svn_client_merge_peg2(source, revision1, revision2, peg_revision,
5819 target_wcpath, recurse, ignore_ancestry, force,
5820 dry_run, NULL, ctx, pool);