* subversion/mod_dav_svn/reports/replay.c
[svn.git] / subversion / libsvn_client / merge.c
blob3d5d530b48c9dd6b3caea4a33affdd4e89c6284f
2 /*
3 * merge.c: merging
5 * ====================================================================
6 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
8 * This software is licensed as described in the file COPYING, which
9 * you should have received as part of this distribution. The terms
10 * are also available at http://subversion.tigris.org/license-1.html.
11 * If newer versions of this license are posted there, you may use a
12 * newer version instead, at your option.
14 * This software consists of voluntary contributions made by many
15 * individuals. For exact contribution history, see the revision
16 * history and logs, available at http://subversion.tigris.org/.
17 * ====================================================================
20 /* ==================================================================== */
24 /*** Includes ***/
26 #include <apr_strings.h>
27 #include <apr_tables.h>
28 #include <apr_hash.h>
29 #include "svn_types.h"
30 #include "svn_hash.h"
31 #include "svn_wc.h"
32 #include "svn_delta.h"
33 #include "svn_diff.h"
34 #include "svn_mergeinfo.h"
35 #include "svn_client.h"
36 #include "svn_string.h"
37 #include "svn_error.h"
38 #include "svn_path.h"
39 #include "svn_io.h"
40 #include "svn_utf.h"
41 #include "svn_pools.h"
42 #include "svn_config.h"
43 #include "svn_props.h"
44 #include "svn_time.h"
45 #include "svn_sorts.h"
46 #include "svn_ra.h"
47 #include "client.h"
48 #include "mergeinfo.h"
49 #include <assert.h>
51 #include "private/svn_wc_private.h"
52 #include "private/svn_mergeinfo_private.h"
54 #include "svn_private_config.h"
56 /*-----------------------------------------------------------------------*/
58 /* MERGEINFO MERGE SOURCE NORMALIZATION
60 * Nearly any helper function herein that accepts two URL/revision
61 * pairs expects one of two things to be true:
63 * 1. that mergeinfo is not being recorded at all for this
64 * operation, or
66 * 2. that the pairs represent two locations along a single line
67 * of version history such that there are no copies in the
68 * history of the object between the locations when treating
69 * the oldest of the two locations as non-inclusive. In other
70 * words, if there is a copy at all between them, there is only
71 * one copy and its source was the oldest of the two locations.
73 * We use svn_ra_get_location_segments() to split a given range of
74 * revisions across an object's history into several which obey these
75 * rules. For example, a merge between r19500 and r27567 of
76 * Subversion's own /tags/1.4.5 directory gets split into sequential
77 * merges of the following location pairs:
79 * [/trunk:19549, /trunk:19523]
80 * (recorded in svn:mergeinfo as /trunk:19500-19523)
82 * [/trunk:19523, /branches/1.4.x:25188]
83 * (recorded in svn:mergeinfo as /branches/1.4.x:19524-25188)
85 * [/branches/1.4.x:25188, /tags/1.4.4@26345]
86 * (recorded in svn:mergeinfo as /tags/1.4.4:25189-26345)
88 * [/tags/1.4.4@26345, /branches/1.4.5@26350]
89 * (recorded in svn:mergeinfo as /branches/1.4.5:26346-26350)
91 * [/branches/1.4.5@26350, /tags/1.4.5@27567]
92 * (recorded in svn:mergeinfo as /tags/1.4.5:26351-27567)
94 * Our helper functions would then operate on one of these location
95 * pairs at a time.
99 /*-----------------------------------------------------------------------*/
101 /*** Utilities ***/
103 /* Sanity check -- ensure that we have valid revisions to look at. */
104 #define ENSURE_VALID_REVISION_KINDS(rev1_kind, rev2_kind) \
107 /* Return SVN_ERR_UNSUPPORTED_FEATURE if URL's scheme does not
108 match the scheme of the url for ADM_ACCESS's path; return
109 SVN_ERR_BAD_URL if no scheme can be found for one or both urls;
110 otherwise return SVN_NO_ERROR. Use ADM_ACCESS's pool for
111 temporary allocation. */
112 static svn_error_t *
113 check_scheme_match(svn_wc_adm_access_t *adm_access, const char *url)
115 const char *path = svn_wc_adm_access_path(adm_access);
116 apr_pool_t *pool = svn_wc_adm_access_pool(adm_access);
117 const svn_wc_entry_t *ent;
118 const char *idx1, *idx2;
120 SVN_ERR(svn_wc_entry(&ent, path, adm_access, TRUE, pool));
122 idx1 = strchr(url, ':');
123 idx2 = strchr(ent->url, ':');
125 if ((idx1 == NULL) && (idx2 == NULL))
127 return svn_error_createf
128 (SVN_ERR_BAD_URL, NULL,
129 _("URLs have no scheme ('%s' and '%s')"), url, ent->url);
131 else if (idx1 == NULL)
133 return svn_error_createf
134 (SVN_ERR_BAD_URL, NULL,
135 _("URL has no scheme: '%s'"), url);
137 else if (idx2 == NULL)
139 return svn_error_createf
140 (SVN_ERR_BAD_URL, NULL,
141 _("URL has no scheme: '%s'"), ent->url);
143 else if (((idx1 - url) != (idx2 - ent->url))
144 || (strncmp(url, ent->url, idx1 - url) != 0))
146 return svn_error_createf
147 (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
148 _("Access scheme mixtures not yet supported ('%s' and '%s')"),
149 url, ent->url);
152 /* else */
154 return SVN_NO_ERROR;
158 /*-----------------------------------------------------------------------*/
160 /*** Repos-Diff Editor Callbacks ***/
163 typedef struct merge_cmd_baton_t {
164 svn_boolean_t force;
165 svn_boolean_t dry_run;
166 svn_boolean_t record_only; /* Whether to only record mergeinfo. */
167 svn_boolean_t sources_ancestral; /* Whether the left-side merge source is
168 an ancestor of the right-side, or
169 vice-versa (history-wise). */
170 svn_boolean_t same_repos; /* Whether the merge source repository
171 is the same repository as the
172 target. Defaults to FALSE if DRY_RUN
173 is TRUE.*/
174 svn_boolean_t mergeinfo_capable; /* Whether the merge source server
175 is capable of Merge Tracking. */
176 svn_boolean_t ignore_ancestry; /* Are we ignoring ancestry (and by
177 extension, mergeinfo)? FALSE if
178 SOURCES_ANCESTRAL is FALSE. */
179 svn_boolean_t target_missing_child; /* Whether working copy target of the
180 merge is missing any immediate
181 children. */
182 svn_boolean_t operative_merge; /* Whether any changes were actually
183 made as a result of this merge. */
184 svn_boolean_t override_set; /* get_mergeinfo_paths set some override
185 mergeginfo - see criteria 3) in
186 get_mergeinfo_paths' comments. */
187 const char *added_path; /* Set to the dir path whenever the
188 dir is added as a child of a
189 versioned dir (dry-run only) */
190 const char *target; /* Working copy target of merge */
191 const char *url; /* The second URL in the merge */
192 svn_client_ctx_t *ctx; /* Client context for callbacks, etc. */
194 /* Whether invocation of the merge_file_added() callback required
195 delegation to the merge_file_changed() function for the file
196 currently being merged. This info is used to detect whether a
197 file on the left side of a 3-way merge actually exists (important
198 because it's created as an empty temp file on disk regardless).*/
199 svn_boolean_t add_necessitated_merge;
201 /* The list of paths for entries we've deleted, used only when in
202 dry_run mode. */
203 apr_hash_t *dry_run_deletions;
205 /* The list of any paths which remained in conflict after a
206 resolution attempt was made. We track this in-memory, rather
207 than just using WC entry state, since the latter doesn't help us
208 when in dry_run mode. */
209 apr_hash_t *conflicted_paths;
211 /* The diff3_cmd in ctx->config, if any, else null. We could just
212 extract this as needed, but since more than one caller uses it,
213 we just set it up when this baton is created. */
214 const char *diff3_cmd;
215 const apr_array_header_t *merge_options;
217 /* RA sessions used throughout a merge operation. Opened/re-parented
218 as needed. */
219 svn_ra_session_t *ra_session1;
220 svn_ra_session_t *ra_session2;
222 /* Flag indicating the fact target has everything merged already,
223 for the sake of children's merge to work it sets itself a dummy
224 merge range of requested_end_rev:requested_end_rev. */
225 svn_boolean_t target_has_dummy_merge_range;
227 apr_pool_t *pool;
228 } merge_cmd_baton_t;
230 apr_hash_t *
231 svn_client__dry_run_deletions(void *merge_cmd_baton)
233 merge_cmd_baton_t *merge_b = merge_cmd_baton;
234 return merge_b->dry_run_deletions;
237 /* Used to avoid spurious notifications (e.g. conflicts) from a merge
238 attempt into an existing target which would have been deleted if we
239 weren't in dry_run mode (issue #2584). Assumes that WCPATH is
240 still versioned (e.g. has an associated entry). */
241 static APR_INLINE svn_boolean_t
242 dry_run_deleted_p(merge_cmd_baton_t *merge_b, const char *wcpath)
244 return (merge_b->dry_run &&
245 apr_hash_get(merge_b->dry_run_deletions, wcpath,
246 APR_HASH_KEY_STRING) != NULL);
249 /* Return whether any WC path was put in conflict by the merge
250 operation corresponding to MERGE_B. */
251 static APR_INLINE svn_boolean_t
252 is_path_conflicted_by_merge(merge_cmd_baton_t *merge_b)
254 return (merge_b->conflicted_paths &&
255 apr_hash_count(merge_b->conflicted_paths) > 0);
258 /* A svn_wc_diff_callbacks2_t function. Used for both file and directory
259 property merges. */
260 static svn_error_t *
261 merge_props_changed(svn_wc_adm_access_t *adm_access,
262 svn_wc_notify_state_t *state,
263 const char *path,
264 const apr_array_header_t *propchanges,
265 apr_hash_t *original_props,
266 void *baton)
268 apr_array_header_t *props;
269 merge_cmd_baton_t *merge_b = baton;
270 svn_client_ctx_t *ctx = merge_b->ctx;
271 apr_pool_t *subpool = svn_pool_create(merge_b->pool);
272 svn_error_t *err;
274 SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props, subpool));
276 /* We only want to merge "regular" version properties: by
277 definition, 'svn merge' shouldn't touch any data within .svn/ */
278 if (props->nelts)
280 /* svn_wc_merge_props() requires ADM_ACCESS to be the access for
281 the parent of PATH. Since the advent of merge tracking,
282 do_directory_merge() may call this (indirectly) with
283 the access for the merge_b->target instead (issue #2781).
284 So, if we have the wrong access, get the right one. */
285 if (svn_path_compare_paths(svn_wc_adm_access_path(adm_access),
286 path) != 0)
287 SVN_ERR(svn_wc_adm_probe_try3(&adm_access, adm_access, path,
288 TRUE, -1, ctx->cancel_func,
289 ctx->cancel_baton, subpool));
291 err = svn_wc_merge_props2(state, path, adm_access, original_props, props,
292 FALSE, merge_b->dry_run, ctx->conflict_func,
293 ctx->conflict_baton, subpool);
294 if (err && (err->apr_err == SVN_ERR_ENTRY_NOT_FOUND
295 || err->apr_err == SVN_ERR_UNVERSIONED_RESOURCE))
297 /* if the entry doesn't exist in the wc, just 'skip' over
298 this part of the tree-delta. */
299 if (state)
300 *state = svn_wc_notify_state_missing;
301 svn_error_clear(err);
302 svn_pool_destroy(subpool);
303 return SVN_NO_ERROR;
305 else if (err)
306 return err;
309 svn_pool_destroy(subpool);
310 return SVN_NO_ERROR;
313 /* Contains any state collected while resolving conflicts. */
314 typedef struct
316 /* The wrapped callback and baton. */
317 svn_wc_conflict_resolver_func_t wrapped_func;
318 void *wrapped_baton;
320 /* The list of any paths which remained in conflict after a
321 resolution attempt was made. */
322 apr_hash_t **conflicted_paths;
324 /* Pool used in notification_receiver() to avoid the iteration
325 sub-pool which is passed in, then subsequently destroyed. */
326 apr_pool_t *pool;
327 } conflict_resolver_baton_t;
329 /* An implementation of the svn_wc_conflict_resolver_func_t interface.
330 We keep a record of paths which remain in conflict after any
331 resolution attempt from BATON->wrapped_func. */
332 static svn_error_t *
333 conflict_resolver(svn_wc_conflict_result_t **result,
334 const svn_wc_conflict_description_t *description,
335 void *baton, apr_pool_t *pool)
337 svn_error_t *err;
338 conflict_resolver_baton_t *conflict_b = baton;
340 if (conflict_b->wrapped_func)
341 err = (*conflict_b->wrapped_func)(result, description,
342 conflict_b->wrapped_baton, pool);
343 else
345 /* If we have no wrapped callback to invoke, then we still need
346 to behave like a proper conflict-callback ourselves. */
347 *result = svn_wc_create_conflict_result(svn_wc_conflict_choose_postpone,
348 NULL, pool);
349 err = SVN_NO_ERROR;
352 /* Keep a record of paths still in conflict after the resolution attempt. */
353 if ((! conflict_b->wrapped_func)
354 || (*result && ((*result)->choice == svn_wc_conflict_choose_postpone)))
356 const char *conflicted_path = apr_pstrdup(conflict_b->pool,
357 description->path);
359 if (*conflict_b->conflicted_paths == NULL)
360 *conflict_b->conflicted_paths = apr_hash_make(conflict_b->pool);
362 apr_hash_set(*conflict_b->conflicted_paths, conflicted_path,
363 APR_HASH_KEY_STRING, conflicted_path);
366 return err;
369 /* A svn_wc_diff_callbacks2_t function. */
370 static svn_error_t *
371 merge_file_changed(svn_wc_adm_access_t *adm_access,
372 svn_wc_notify_state_t *content_state,
373 svn_wc_notify_state_t *prop_state,
374 const char *mine,
375 const char *older,
376 const char *yours,
377 svn_revnum_t older_rev,
378 svn_revnum_t yours_rev,
379 const char *mimetype1,
380 const char *mimetype2,
381 const apr_array_header_t *prop_changes,
382 apr_hash_t *original_props,
383 void *baton)
385 merge_cmd_baton_t *merge_b = baton;
386 apr_pool_t *subpool = svn_pool_create(merge_b->pool);
387 svn_boolean_t merge_required = TRUE;
388 enum svn_wc_merge_outcome_t merge_outcome;
390 /* Easy out: no access baton means there ain't no merge target */
391 if (adm_access == NULL)
393 if (content_state)
394 *content_state = svn_wc_notify_state_missing;
395 if (prop_state)
396 *prop_state = svn_wc_notify_state_missing;
397 svn_pool_destroy(subpool);
398 return SVN_NO_ERROR;
401 /* Other easy outs: if the merge target isn't under version
402 control, or is just missing from disk, fogettaboutit. There's no
403 way svn_wc_merge3() can do the merge. */
405 const svn_wc_entry_t *entry;
406 svn_node_kind_t kind;
408 SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool));
409 SVN_ERR(svn_io_check_path(mine, &kind, subpool));
411 /* ### a future thought: if the file is under version control,
412 but the working file is missing, maybe we can 'restore' the
413 working file from the text-base, and then allow the merge to run? */
415 if ((! entry) || (kind != svn_node_file))
417 if (content_state)
418 *content_state = svn_wc_notify_state_missing;
419 if (prop_state)
420 *prop_state = svn_wc_notify_state_missing;
421 svn_pool_destroy(subpool);
422 return SVN_NO_ERROR;
426 /* ### TODO: Thwart attempts to merge into a path that has
427 ### unresolved conflicts. This needs to be smart enough to deal
428 ### with tree conflicts!
429 if (is_path_conflicted_by_merge(merge_b, mine))
431 *content_state = svn_wc_notify_state_conflicted;
432 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
433 _("Path '%s' is in conflict, and must be "
434 "resolved before the remainder of the "
435 "requested merge can be applied"), mine);
439 /* This callback is essentially no more than a wrapper around
440 svn_wc_merge3(). Thank goodness that all the
441 diff-editor-mechanisms are doing the hard work of getting the
442 fulltexts! */
444 /* Do property merge before text merge so that keyword expansion takes
445 into account the new property values. */
446 if (prop_changes->nelts > 0)
447 SVN_ERR(merge_props_changed(adm_access, prop_state, mine, prop_changes,
448 original_props, baton));
449 else
450 if (prop_state)
451 *prop_state = svn_wc_notify_state_unchanged;
453 if (older)
455 svn_boolean_t has_local_mods;
456 SVN_ERR(svn_wc_text_modified_p(&has_local_mods, mine, FALSE,
457 adm_access, subpool));
459 /* Special case: if a binary file's working file is
460 exactly identical to the 'left' side of the merge, then don't
461 allow svn_wc_merge to produce a conflict. Instead, just
462 overwrite the working file with the 'right' side of the
463 merge. Why'd we check for local mods above? Because we want
464 to do a different notification depending on whether or not
465 the file was locally modified.
467 Alternately, if the 'left' side of the merge doesn't exist in
468 the repository, and the 'right' side of the merge is
469 identical to the WC, pretend we did the merge (a no-op). */
470 if ((mimetype1 && svn_mime_type_is_binary(mimetype1))
471 || (mimetype2 && svn_mime_type_is_binary(mimetype2)))
473 /* For adds, the 'left' side of the merge doesn't exist. */
474 svn_boolean_t older_revision_exists =
475 !merge_b->add_necessitated_merge;
476 svn_boolean_t same_contents;
477 SVN_ERR(svn_io_files_contents_same_p(&same_contents,
478 (older_revision_exists ?
479 older : yours),
480 mine, subpool));
481 if (same_contents)
483 if (older_revision_exists && !merge_b->dry_run)
484 SVN_ERR(svn_io_file_rename(yours, mine, subpool));
485 merge_outcome = svn_wc_merge_merged;
486 merge_required = FALSE;
490 if (merge_required)
492 /* xgettext: the '.working', '.merge-left.r%ld' and
493 '.merge-right.r%ld' strings are used to tag onto a file
494 name in case of a merge conflict */
495 const char *target_label = _(".working");
496 const char *left_label = apr_psprintf(subpool,
497 _(".merge-left.r%ld"),
498 older_rev);
499 const char *right_label = apr_psprintf(subpool,
500 _(".merge-right.r%ld"),
501 yours_rev);
502 conflict_resolver_baton_t conflict_baton =
503 { merge_b->ctx->conflict_func, merge_b->ctx->conflict_baton,
504 &merge_b->conflicted_paths, merge_b->pool };
505 SVN_ERR(svn_wc_merge3(&merge_outcome,
506 older, yours, mine, adm_access,
507 left_label, right_label, target_label,
508 merge_b->dry_run, merge_b->diff3_cmd,
509 merge_b->merge_options, prop_changes,
510 conflict_resolver, &conflict_baton,
511 subpool));
514 if (content_state)
516 if (merge_outcome == svn_wc_merge_conflict)
517 *content_state = svn_wc_notify_state_conflicted;
518 else if (has_local_mods
519 && merge_outcome != svn_wc_merge_unchanged)
520 *content_state = svn_wc_notify_state_merged;
521 else if (merge_outcome == svn_wc_merge_merged)
522 *content_state = svn_wc_notify_state_changed;
523 else if (merge_outcome == svn_wc_merge_no_merge)
524 *content_state = svn_wc_notify_state_missing;
525 else /* merge_outcome == svn_wc_merge_unchanged */
526 *content_state = svn_wc_notify_state_unchanged;
530 svn_pool_destroy(subpool);
531 return SVN_NO_ERROR;
534 /* A svn_wc_diff_callbacks2_t function. */
535 static svn_error_t *
536 merge_file_added(svn_wc_adm_access_t *adm_access,
537 svn_wc_notify_state_t *content_state,
538 svn_wc_notify_state_t *prop_state,
539 const char *mine,
540 const char *older,
541 const char *yours,
542 svn_revnum_t rev1,
543 svn_revnum_t rev2,
544 const char *mimetype1,
545 const char *mimetype2,
546 const apr_array_header_t *prop_changes,
547 apr_hash_t *original_props,
548 void *baton)
550 merge_cmd_baton_t *merge_b = baton;
551 apr_pool_t *subpool = svn_pool_create(merge_b->pool);
552 svn_node_kind_t kind;
553 const char *copyfrom_url;
554 const char *child;
555 int i;
556 apr_hash_t *new_props;
558 /* In most cases, we just leave prop_state as unknown, and let the
559 content_state what happened, so we set prop_state here to avoid that
560 below. */
561 if (prop_state)
562 *prop_state = svn_wc_notify_state_unknown;
564 /* Apply the prop changes to a new hash table. */
565 new_props = apr_hash_copy(subpool, original_props);
566 for (i = 0; i < prop_changes->nelts; ++i)
568 const svn_prop_t *prop = &APR_ARRAY_IDX(prop_changes, i, svn_prop_t);
569 apr_hash_set(new_props, prop->name, APR_HASH_KEY_STRING, prop->value);
572 /* Easy out: if we have no adm_access for the parent directory,
573 then this portion of the tree-delta "patch" must be inapplicable.
574 Send a 'missing' state back; the repos-diff editor should then
575 send a 'skip' notification. */
576 if (! adm_access)
578 if (merge_b->dry_run && merge_b->added_path
579 && svn_path_is_child(merge_b->added_path, mine, subpool))
581 if (content_state)
582 *content_state = svn_wc_notify_state_changed;
583 if (prop_state && apr_hash_count(new_props))
584 *prop_state = svn_wc_notify_state_changed;
586 else
587 *content_state = svn_wc_notify_state_missing;
588 svn_pool_destroy(subpool);
589 return SVN_NO_ERROR;
592 SVN_ERR(svn_io_check_path(mine, &kind, subpool));
593 switch (kind)
595 case svn_node_none:
597 const svn_wc_entry_t *entry;
598 SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool));
599 if (entry && entry->schedule != svn_wc_schedule_delete)
601 /* It's versioned but missing. */
602 if (content_state)
603 *content_state = svn_wc_notify_state_obstructed;
604 svn_pool_destroy(subpool);
605 return SVN_NO_ERROR;
607 if (! merge_b->dry_run)
609 child = svn_path_is_child(merge_b->target, mine, subpool);
610 if (child != NULL)
611 copyfrom_url = svn_path_url_add_component(merge_b->url, child,
612 subpool);
613 else
614 copyfrom_url = merge_b->url;
615 SVN_ERR(check_scheme_match(adm_access, copyfrom_url));
617 /* Since 'mine' doesn't exist, and this is
618 'merge_file_added', I hope it's safe to assume that
619 'older' is empty, and 'yours' is the full file. Merely
620 copying 'yours' to 'mine', isn't enough; we need to get
621 the whole text-base and props installed too, just as if
622 we had called 'svn cp wc wc'. */
623 SVN_ERR(svn_wc_add_repos_file2(mine, adm_access, yours, NULL,
624 new_props, NULL, copyfrom_url,
625 rev2, subpool));
627 if (content_state)
628 *content_state = svn_wc_notify_state_changed;
629 if (prop_state && apr_hash_count(new_props))
630 *prop_state = svn_wc_notify_state_changed;
632 break;
633 case svn_node_dir:
634 if (content_state)
636 /* directory already exists, is it under version control? */
637 const svn_wc_entry_t *entry;
638 SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool));
640 if (entry && dry_run_deleted_p(merge_b, mine))
641 *content_state = svn_wc_notify_state_changed;
642 else
643 /* this will make the repos_editor send a 'skipped' message */
644 *content_state = svn_wc_notify_state_obstructed;
646 break;
647 case svn_node_file:
649 /* file already exists, is it under version control? */
650 const svn_wc_entry_t *entry;
651 SVN_ERR(svn_wc_entry(&entry, mine, adm_access, FALSE, subpool));
653 /* If it's an unversioned file, don't touch it. If it's scheduled
654 for deletion, then rm removed it from the working copy and the
655 user must have recreated it, don't touch it */
656 if (!entry || entry->schedule == svn_wc_schedule_delete)
658 /* this will make the repos_editor send a 'skipped' message */
659 if (content_state)
660 *content_state = svn_wc_notify_state_obstructed;
662 else
664 if (dry_run_deleted_p(merge_b, mine))
666 if (content_state)
667 *content_state = svn_wc_notify_state_changed;
669 else
671 /* Indicate that we merge because of an add to handle a
672 special case for binary files with no local mods. */
673 merge_b->add_necessitated_merge = TRUE;
675 SVN_ERR(merge_file_changed(adm_access, content_state,
676 prop_state, mine, older, yours,
677 rev1, rev2,
678 mimetype1, mimetype2,
679 prop_changes, original_props,
680 baton));
682 /* Reset the state so that the baton can safely be reused
683 in subsequent ops occurring during this merge. */
684 merge_b->add_necessitated_merge = FALSE;
687 break;
689 default:
690 if (content_state)
691 *content_state = svn_wc_notify_state_unknown;
692 break;
695 svn_pool_destroy(subpool);
696 return SVN_NO_ERROR;
699 /* A svn_wc_diff_callbacks2_t function. */
700 static svn_error_t *
701 merge_file_deleted(svn_wc_adm_access_t *adm_access,
702 svn_wc_notify_state_t *state,
703 const char *mine,
704 const char *older,
705 const char *yours,
706 const char *mimetype1,
707 const char *mimetype2,
708 apr_hash_t *original_props,
709 void *baton)
711 merge_cmd_baton_t *merge_b = baton;
712 apr_pool_t *subpool = svn_pool_create(merge_b->pool);
713 svn_node_kind_t kind;
714 svn_wc_adm_access_t *parent_access;
715 const char *parent_path;
716 svn_error_t *err;
718 /* Easy out: if we have no adm_access for the parent directory,
719 then this portion of the tree-delta "patch" must be inapplicable.
720 Send a 'missing' state back; the repos-diff editor should then
721 send a 'skip' notification. */
722 if (! adm_access)
724 if (state)
725 *state = svn_wc_notify_state_missing;
726 svn_pool_destroy(subpool);
727 return SVN_NO_ERROR;
730 SVN_ERR(svn_io_check_path(mine, &kind, subpool));
731 switch (kind)
733 case svn_node_file:
734 svn_path_split(mine, &parent_path, NULL, subpool);
735 SVN_ERR(svn_wc_adm_retrieve(&parent_access, adm_access, parent_path,
736 subpool));
737 /* Passing NULL for the notify_func and notify_baton because
738 repos_diff.c:delete_entry() will do it for us. */
739 err = svn_client__wc_delete(mine, parent_access, merge_b->force,
740 merge_b->dry_run, FALSE, NULL, NULL,
741 merge_b->ctx, subpool);
742 if (err && state)
744 *state = svn_wc_notify_state_obstructed;
745 svn_error_clear(err);
747 else if (state)
749 *state = svn_wc_notify_state_changed;
751 break;
752 case svn_node_dir:
753 if (state)
754 *state = svn_wc_notify_state_obstructed;
755 break;
756 case svn_node_none:
757 /* file is already non-existent, this is a no-op. */
758 if (state)
759 *state = svn_wc_notify_state_missing;
760 break;
761 default:
762 if (state)
763 *state = svn_wc_notify_state_unknown;
764 break;
767 svn_pool_destroy(subpool);
768 return SVN_NO_ERROR;
771 /* A svn_wc_diff_callbacks2_t function. */
772 static svn_error_t *
773 merge_dir_added(svn_wc_adm_access_t *adm_access,
774 svn_wc_notify_state_t *state,
775 const char *path,
776 svn_revnum_t rev,
777 void *baton)
779 merge_cmd_baton_t *merge_b = baton;
780 apr_pool_t *subpool = svn_pool_create(merge_b->pool);
781 svn_node_kind_t kind;
782 const svn_wc_entry_t *entry;
783 const char *copyfrom_url, *child;
785 /* Easy out: if we have no adm_access for the parent directory,
786 then this portion of the tree-delta "patch" must be inapplicable.
787 Send a 'missing' state back; the repos-diff editor should then
788 send a 'skip' notification. */
789 if (! adm_access)
791 if (state)
793 if (merge_b->dry_run && merge_b->added_path
794 && svn_path_is_child(merge_b->added_path, path, subpool))
795 *state = svn_wc_notify_state_changed;
796 else
797 *state = svn_wc_notify_state_missing;
799 svn_pool_destroy(subpool);
800 return SVN_NO_ERROR;
803 child = svn_path_is_child(merge_b->target, path, subpool);
804 assert(child != NULL);
805 copyfrom_url = svn_path_url_add_component(merge_b->url, child, subpool);
806 SVN_ERR(check_scheme_match(adm_access, copyfrom_url));
808 SVN_ERR(svn_io_check_path(path, &kind, subpool));
809 switch (kind)
811 case svn_node_none:
812 SVN_ERR(svn_wc_entry(&entry, path, adm_access, FALSE, subpool));
813 if (entry && entry->schedule != svn_wc_schedule_delete)
815 /* Versioned but missing */
816 if (state)
817 *state = svn_wc_notify_state_obstructed;
818 svn_pool_destroy(subpool);
819 return SVN_NO_ERROR;
821 if (merge_b->dry_run)
822 merge_b->added_path = apr_pstrdup(merge_b->pool, path);
823 else
825 SVN_ERR(svn_io_make_dir_recursively(path, subpool));
826 SVN_ERR(svn_wc_add2(path, adm_access,
827 copyfrom_url, rev,
828 merge_b->ctx->cancel_func,
829 merge_b->ctx->cancel_baton,
830 NULL, NULL, /* don't pass notification func! */
831 subpool));
834 if (state)
835 *state = svn_wc_notify_state_changed;
836 break;
837 case svn_node_dir:
838 /* Adding an unversioned directory doesn't destroy data */
839 SVN_ERR(svn_wc_entry(&entry, path, adm_access, TRUE, subpool));
840 if (! entry || entry->schedule == svn_wc_schedule_delete)
842 if (!merge_b->dry_run)
843 SVN_ERR(svn_wc_add2(path, adm_access,
844 copyfrom_url, rev,
845 merge_b->ctx->cancel_func,
846 merge_b->ctx->cancel_baton,
847 NULL, NULL, /* no notification func! */
848 subpool));
849 else
850 merge_b->added_path = apr_pstrdup(merge_b->pool, path);
851 if (state)
852 *state = svn_wc_notify_state_changed;
854 else if (state)
856 if (dry_run_deleted_p(merge_b, path))
857 *state = svn_wc_notify_state_changed;
858 else
859 *state = svn_wc_notify_state_obstructed;
861 break;
862 case svn_node_file:
863 if (merge_b->dry_run)
864 merge_b->added_path = NULL;
866 if (state)
868 SVN_ERR(svn_wc_entry(&entry, path, adm_access, FALSE, subpool));
870 if (entry && dry_run_deleted_p(merge_b, path))
871 /* ### TODO: Retain record of this dir being added to
872 ### avoid problems from subsequent edits which try to
873 ### add children. */
874 *state = svn_wc_notify_state_changed;
875 else
876 *state = svn_wc_notify_state_obstructed;
878 break;
879 default:
880 if (merge_b->dry_run)
881 merge_b->added_path = NULL;
882 if (state)
883 *state = svn_wc_notify_state_unknown;
884 break;
887 svn_pool_destroy(subpool);
888 return SVN_NO_ERROR;
891 /* A svn_wc_diff_callbacks2_t function. */
892 static svn_error_t *
893 merge_dir_deleted(svn_wc_adm_access_t *adm_access,
894 svn_wc_notify_state_t *state,
895 const char *path,
896 void *baton)
898 merge_cmd_baton_t *merge_b = baton;
899 apr_pool_t *subpool = svn_pool_create(merge_b->pool);
900 svn_node_kind_t kind;
901 svn_wc_adm_access_t *parent_access;
902 const char *parent_path;
903 svn_error_t *err;
905 /* Easy out: if we have no adm_access for the parent directory,
906 then this portion of the tree-delta "patch" must be inapplicable.
907 Send a 'missing' state back; the repos-diff editor should then
908 send a 'skip' notification. */
909 if (! adm_access)
911 if (state)
912 *state = svn_wc_notify_state_missing;
913 svn_pool_destroy(subpool);
914 return SVN_NO_ERROR;
917 SVN_ERR(svn_io_check_path(path, &kind, subpool));
918 switch (kind)
920 case svn_node_dir:
922 svn_path_split(path, &parent_path, NULL, subpool);
923 SVN_ERR(svn_wc_adm_retrieve(&parent_access, adm_access, parent_path,
924 subpool));
925 /* Passing NULL for the notify_func and notify_baton because
926 repos_diff.c:delete_entry() will do it for us. */
927 err = svn_client__wc_delete(path, parent_access, merge_b->force,
928 merge_b->dry_run, FALSE,
929 NULL, NULL,
930 merge_b->ctx, subpool);
931 if (err && state)
933 *state = svn_wc_notify_state_obstructed;
934 svn_error_clear(err);
936 else if (state)
938 *state = svn_wc_notify_state_changed;
941 break;
942 case svn_node_file:
943 if (state)
944 *state = svn_wc_notify_state_obstructed;
945 break;
946 case svn_node_none:
947 /* dir is already non-existent, this is a no-op. */
948 if (state)
949 *state = svn_wc_notify_state_missing;
950 break;
951 default:
952 if (state)
953 *state = svn_wc_notify_state_unknown;
954 break;
957 svn_pool_destroy(subpool);
958 return SVN_NO_ERROR;
961 /* The main callback table for 'svn merge'. */
962 static const svn_wc_diff_callbacks2_t
963 merge_callbacks =
965 merge_file_changed,
966 merge_file_added,
967 merge_file_deleted,
968 merge_dir_added,
969 merge_dir_deleted,
970 merge_props_changed
974 /*-----------------------------------------------------------------------*/
976 /*** Merge Notification ***/
979 /* Contains any state collected while receiving path notifications. */
980 typedef struct
982 /* The wrapped callback and baton. */
983 svn_wc_notify_func2_t wrapped_func;
984 void *wrapped_baton;
986 /* The number of notifications received. */
987 apr_uint32_t nbr_notifications;
989 /* The number of operative notifications received. */
990 apr_uint32_t nbr_operative_notifications;
992 /* The list of merged paths. */
993 apr_hash_t *merged_paths;
995 /* The list of any skipped paths, which should be examined and
996 cleared after each invocation of the callback. */
997 apr_hash_t *skipped_paths;
999 /* Flag indicating whether it is a single file merge or not. */
1000 svn_boolean_t is_single_file_merge;
1002 /* Depth first ordered list of paths that needs special care while merging.
1003 This defaults to NULL. For 'same_url' merge alone we set it to
1004 proper array. This is used by notification_receiver to put a
1005 merge notification begin lines. */
1006 apr_array_header_t *children_with_mergeinfo;
1008 /* The index in CHILDREN_WITH_MERGEINFO where we found the nearest ancestor
1009 for merged path. Default value is '-1'.*/
1010 int cur_ancestor_index;
1012 /* We use this to make a decision on merge begin line notifications. */
1013 merge_cmd_baton_t *merge_b;
1015 /* Pool used in notification_receiver() to avoid the iteration
1016 sub-pool which is passed in, then subsequently destroyed. */
1017 apr_pool_t *pool;
1019 } notification_receiver_baton_t;
1022 /* Finds a nearest ancestor in CHILDREN_WITH_MERGEINFO for PATH.
1023 CHILDREN_WITH_MERGEINFO is expected to be sorted in Depth first
1024 order of path. Nearest ancestor's index from
1025 CHILDREN_WITH_MERGEINFO is returned. */
1026 static int
1027 find_nearest_ancestor(apr_array_header_t *children_with_mergeinfo,
1028 const char *path)
1030 int i;
1031 int ancestor_index = 0;
1033 /* This if condition is not needed as this function should be used
1034 from the context of same_url merge where CHILDREN_WITH_MERGEINFO
1035 will not be NULL and of size atleast 1. We have this if condition
1036 just to protect the wrong caller. */
1037 if (!children_with_mergeinfo)
1038 return 0;
1039 for (i = 0; i < children_with_mergeinfo->nelts; i++)
1041 svn_client__merge_path_t *child =
1042 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
1043 if (svn_path_is_ancestor(child->path, path))
1044 ancestor_index = i;
1046 return ancestor_index;
1050 /* Our svn_wc_notify_func2_t wrapper.*/
1051 static void
1052 notification_receiver(void *baton, const svn_wc_notify_t *notify,
1053 apr_pool_t *pool)
1055 notification_receiver_baton_t *notify_b = baton;
1056 svn_boolean_t is_operative_notification = FALSE;
1058 /* Is the notification the result of a real operative merge? */
1059 if (notify->content_state == svn_wc_notify_state_conflicted
1060 || notify->content_state == svn_wc_notify_state_merged
1061 || notify->content_state == svn_wc_notify_state_changed
1062 || notify->prop_state == svn_wc_notify_state_conflicted
1063 || notify->prop_state == svn_wc_notify_state_merged
1064 || notify->prop_state == svn_wc_notify_state_changed
1065 || notify->action == svn_wc_notify_update_add)
1067 notify_b->nbr_operative_notifications++;
1068 is_operative_notification = TRUE;
1071 /* If our merge sources are ancestors of one another... */
1072 if (notify_b->merge_b->sources_ancestral)
1074 notify_b->nbr_notifications++;
1076 /* See if this is an operative directory merge. */
1077 if (!(notify_b->is_single_file_merge) && is_operative_notification)
1079 int new_nearest_ancestor_index =
1080 find_nearest_ancestor(notify_b->children_with_mergeinfo,
1081 notify->path);
1082 if (new_nearest_ancestor_index != notify_b->cur_ancestor_index)
1084 svn_client__merge_path_t *child =
1085 APR_ARRAY_IDX(notify_b->children_with_mergeinfo,
1086 new_nearest_ancestor_index,
1087 svn_client__merge_path_t *);
1088 notify_b->cur_ancestor_index = new_nearest_ancestor_index;
1089 if (!child->absent && child->remaining_ranges->nelts > 0
1090 && !(new_nearest_ancestor_index == 0
1091 && notify_b->merge_b->target_has_dummy_merge_range))
1093 svn_wc_notify_t *notify_merge_begin;
1094 notify_merge_begin =
1095 svn_wc_create_notify(child->path,
1096 svn_wc_notify_merge_begin, pool);
1097 notify_merge_begin->merge_range =
1098 APR_ARRAY_IDX(child->remaining_ranges, 0,
1099 svn_merge_range_t *);
1100 if (notify_b->wrapped_func)
1101 (*notify_b->wrapped_func)(notify_b->wrapped_baton,
1102 notify_merge_begin, pool);
1107 if (notify->content_state == svn_wc_notify_state_merged
1108 || notify->content_state == svn_wc_notify_state_changed
1109 || notify->prop_state == svn_wc_notify_state_merged
1110 || notify->prop_state == svn_wc_notify_state_changed
1111 || notify->action == svn_wc_notify_update_add)
1113 const char *merged_path = apr_pstrdup(notify_b->pool, notify->path);
1115 if (notify_b->merged_paths == NULL)
1116 notify_b->merged_paths = apr_hash_make(notify_b->pool);
1118 apr_hash_set(notify_b->merged_paths, merged_path,
1119 APR_HASH_KEY_STRING, merged_path);
1122 if (notify->action == svn_wc_notify_skip)
1124 const char *skipped_path = apr_pstrdup(notify_b->pool, notify->path);
1126 if (notify_b->skipped_paths == NULL)
1127 notify_b->skipped_paths = apr_hash_make(notify_b->pool);
1129 apr_hash_set(notify_b->skipped_paths, skipped_path,
1130 APR_HASH_KEY_STRING, skipped_path);
1133 /* Otherwise, our merge sources aren't ancestors of one another. */
1134 else if (!(notify_b->is_single_file_merge)
1135 && notify_b->nbr_operative_notifications == 1
1136 && is_operative_notification)
1138 svn_wc_notify_t *notify_merge_begin;
1139 notify_merge_begin = svn_wc_create_notify(notify_b->merge_b->target,
1140 svn_wc_notify_merge_begin,
1141 pool);
1142 if (notify_b->wrapped_func)
1143 (*notify_b->wrapped_func)(notify_b->wrapped_baton, notify_merge_begin,
1144 pool);
1147 if (notify_b->wrapped_func)
1148 (*notify_b->wrapped_func)(notify_b->wrapped_baton, notify, pool);
1152 /*-----------------------------------------------------------------------*/
1154 /*** Determining What Remains To Be Merged ***/
1157 /* Set *REQUESTED_RANGELIST to a list of revision ranges consisting of
1158 a single requested range (between URL1@REVISION1 and
1159 URL2@REVISION2), minus merges which originated from TARGET_URL
1160 which were already recorded as performed within that range.
1162 See `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements
1163 around the values of URL1, REVISION1, URL2, and REVISION2.
1165 Use SOURCE_ROOT_URL for all the various relative-mergeinfo-path
1166 calculations needed to do this work.
1168 RA_SESSION is an RA session whose session URL is the root URL of
1169 the source repository.
1171 NOTE: This should only be called when honoring mergeinfo.
1173 ### FIXME: I strongly suspect that these calculations are
1174 ### rename-ignorant, not accounting for the situation where the
1175 ### item at TARGET_URL back when merges were from it to our current
1176 ### merge source is not the same item at TARGET_URL now that we're
1177 ### trying to merge from that current merge source. -- cmpilato
1179 static svn_error_t *
1180 filter_reflected_revisions(apr_array_header_t **requested_rangelist,
1181 const char *source_root_url,
1182 const char *url1,
1183 svn_revnum_t revision1,
1184 const char *url2,
1185 svn_revnum_t revision2,
1186 svn_boolean_t inheritable,
1187 const char *target_url,
1188 svn_ra_session_t *ra_session,
1189 svn_client_ctx_t *ctx,
1190 apr_pool_t *pool)
1192 apr_array_header_t *src_rangelist_for_tgt = NULL;
1193 apr_hash_t *added_mergeinfo, *deleted_mergeinfo,
1194 *start_mergeinfo, *end_mergeinfo;
1195 svn_merge_range_t *range = apr_pcalloc(pool, sizeof(*range));
1196 svn_revnum_t min_rev = MIN(revision1, revision2);
1197 svn_revnum_t max_rev = MAX(revision1, revision2);
1198 const char *min_url = (revision1 < revision2) ? url1 : url2;
1199 const char *max_url = (revision1 < revision2) ? url2 : url1;
1200 const char *min_rel_path, *max_rel_path;
1202 SVN_ERR(svn_client__path_relative_to_root(&min_rel_path, min_url,
1203 source_root_url, FALSE, ra_session,
1204 NULL, pool));
1205 SVN_ERR(svn_client__path_relative_to_root(&max_rel_path, max_url,
1206 source_root_url, FALSE, ra_session,
1207 NULL, pool));
1209 /* Find any mergeinfo for TARGET_URL added to the line of history
1210 between URL1@REVISION1 and URL2@REVISION2. */
1211 SVN_ERR(svn_client__get_repos_mergeinfo(ra_session, &start_mergeinfo,
1212 min_rel_path, min_rev,
1213 svn_mergeinfo_inherited, TRUE,
1214 pool));
1215 SVN_ERR(svn_client__get_repos_mergeinfo(ra_session, &end_mergeinfo,
1216 max_rel_path, max_rev,
1217 svn_mergeinfo_inherited, TRUE,
1218 pool));
1220 SVN_ERR(svn_mergeinfo_diff(&deleted_mergeinfo, &added_mergeinfo,
1221 start_mergeinfo, end_mergeinfo,
1222 FALSE, pool));
1224 if (added_mergeinfo)
1226 const char *mergeinfo_path;
1227 SVN_ERR(svn_client__path_relative_to_root(&mergeinfo_path, target_url,
1228 source_root_url, TRUE,
1229 ra_session, NULL, pool));
1230 src_rangelist_for_tgt = apr_hash_get(added_mergeinfo, mergeinfo_path,
1231 APR_HASH_KEY_STRING);
1234 /* Create a single-item list of ranges with our one requested range
1235 in it, and then remove overlapping revision ranges from that range. */
1236 *requested_rangelist = apr_array_make(pool, 1, sizeof(range));
1237 range->start = revision1;
1238 range->end = revision2;
1239 range->inheritable = inheritable;
1240 APR_ARRAY_PUSH(*requested_rangelist, svn_merge_range_t *) = range;
1241 if (src_rangelist_for_tgt)
1242 SVN_ERR(svn_rangelist_remove(requested_rangelist, src_rangelist_for_tgt,
1243 *requested_rangelist,
1244 FALSE, pool));
1245 return SVN_NO_ERROR;
1248 /* Calculate a rangelist of svn_merge_range_t *'s -- for use by
1249 drive_merge_report_editor()'s application of the editor to the WC
1250 -- by subtracting revisions which have already been merged from
1251 MERGEINFO_PATH into the working copy from the requested range(s)
1252 REQUESTED_MERGE, and storing what's left in REMAINING_RANGES.
1253 TARGET_MERGEINFO may be NULL.
1255 NOTE: This should only be called when honoring mergeinfo.
1257 static svn_error_t *
1258 filter_merged_revisions(apr_array_header_t **remaining_ranges,
1259 const char *mergeinfo_path,
1260 apr_hash_t *target_mergeinfo,
1261 apr_hash_t *implicit_mergeinfo,
1262 apr_array_header_t *requested_merge,
1263 svn_boolean_t is_rollback,
1264 const svn_wc_entry_t *entry,
1265 apr_pool_t *pool)
1267 apr_array_header_t *target_rangelist = NULL;
1268 apr_hash_t *mergeinfo;
1270 if (is_rollback)
1272 mergeinfo = svn_mergeinfo_dup(implicit_mergeinfo, pool);
1273 if (target_mergeinfo)
1274 SVN_ERR(svn_mergeinfo_merge(mergeinfo, target_mergeinfo, pool));
1275 target_rangelist = apr_hash_get(mergeinfo,
1276 mergeinfo_path, APR_HASH_KEY_STRING);
1277 if (target_rangelist)
1279 /* Return the intersection of the revs which are both
1280 already represented by the WC and are requested for
1281 revert. The revert range and will need to be reversed
1282 for our APIs to work properly, as will the output for the
1283 revert to work properly. */
1284 requested_merge = svn_rangelist_dup(requested_merge, pool);
1285 SVN_ERR(svn_rangelist_reverse(requested_merge, pool));
1286 SVN_ERR(svn_rangelist_intersect(remaining_ranges,
1287 target_rangelist,
1288 requested_merge, pool));
1289 SVN_ERR(svn_rangelist_reverse(*remaining_ranges, pool));
1291 else
1293 *remaining_ranges =
1294 apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
1297 else
1299 *remaining_ranges = requested_merge;
1301 /* ### TODO: Which evil shall we choose?
1303 ### If we allow all forward-merges not already found in recorded
1304 ### mergeinfo, we destroy the ability to, say, merge the whole of a
1305 ### branch to the trunk while automatically ignoring the revisions
1306 ### common to both. That's bad.
1308 ### If we allow only forward-merges not found in either recorded
1309 ### mergeinfo or implicit mergeinfo (natural history), then the
1310 ### previous scenario works great, but we can't reverse-merge a
1311 ### previous change made to our line of history and then remake it
1312 ### (because the reverse-merge will leave no mergeinfo trace, and
1313 ### the remake-it attempt will still find the original change in
1314 ### natural mergeinfo. But you know, that we happen to use 'merge'
1315 ### for revision undoing is somewhat unnatural anyway, so I'm
1316 ### finding myself having little interest in caring too much about
1317 ### this. That said, if we had a way of storing reverse merge
1318 ### ranges, we'd be in good shape either way.
1320 #ifdef SVN_MERGE__ALLOW_ALL_FORWARD_MERGES_FROM_SELF
1321 if (target_mergeinfo)
1322 target_rangelist = apr_hash_get(target_mergeinfo,
1323 mergeinfo_path, APR_HASH_KEY_STRING);
1324 #else
1325 mergeinfo = svn_mergeinfo_dup(implicit_mergeinfo, pool);
1326 if (target_mergeinfo)
1327 SVN_ERR(svn_mergeinfo_merge(mergeinfo, target_mergeinfo, pool));
1328 target_rangelist = apr_hash_get(mergeinfo,
1329 mergeinfo_path, APR_HASH_KEY_STRING);
1330 #endif
1332 if (target_rangelist)
1333 SVN_ERR(svn_rangelist_remove(remaining_ranges, target_rangelist,
1334 requested_merge, FALSE, pool));
1336 return SVN_NO_ERROR;
1339 /* Populate *REMAINING_RANGES with a list of revision ranges
1340 constructed by taking after removing reflective merge
1341 ranges and already-merged ranges from *RANGE. Cascade URL1,
1342 REVISION1, UR2, REVISION2, TARGET_MERGEINFO, IS_ROLLBACK, REL_PATH,
1343 RA_SESSION, ENTRY, CTX, and POOL.
1345 See `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements
1346 around the values of URL1, REVISION1, URL2, and REVISION2.
1348 NOTE: This should only be called when honoring mergeinfo.
1350 static svn_error_t *
1351 calculate_remaining_ranges(apr_array_header_t **remaining_ranges,
1352 const char *source_root_url,
1353 const char *url1,
1354 svn_revnum_t revision1,
1355 const char *url2,
1356 svn_revnum_t revision2,
1357 svn_boolean_t inheritable,
1358 apr_hash_t *target_mergeinfo,
1359 apr_hash_t *implicit_mergeinfo,
1360 svn_ra_session_t *ra_session,
1361 const svn_wc_entry_t *entry,
1362 svn_client_ctx_t *ctx,
1363 apr_pool_t *pool)
1365 apr_array_header_t *requested_rangelist;
1366 const char *old_url;
1367 const char *mergeinfo_path;
1368 const char *primary_url = (revision1 < revision2) ? url2 : url1;
1370 /* Determine which of the requested ranges to consider merging... */
1371 SVN_ERR(svn_ra_get_session_url(ra_session, &old_url, pool));
1372 SVN_ERR(svn_ra_reparent(ra_session, source_root_url, pool));
1373 SVN_ERR(filter_reflected_revisions(&requested_rangelist, source_root_url,
1374 url1, revision1, url2, revision2,
1375 inheritable, entry->url,
1376 ra_session, ctx, pool));
1377 SVN_ERR(svn_ra_reparent(ra_session, old_url, pool));
1379 /* ...and of those ranges, determine which ones actually still
1380 need merging. */
1381 SVN_ERR(svn_client__path_relative_to_root(&mergeinfo_path, primary_url,
1382 source_root_url, TRUE,
1383 ra_session, NULL, pool));
1384 SVN_ERR(filter_merged_revisions(remaining_ranges, mergeinfo_path,
1385 target_mergeinfo, implicit_mergeinfo,
1386 requested_rangelist,
1387 (revision1 > revision2), entry, pool));
1388 return SVN_NO_ERROR;
1392 static svn_error_t *
1393 get_full_mergeinfo(apr_hash_t **recorded_mergeinfo,
1394 apr_hash_t **implicit_mergeinfo,
1395 const svn_wc_entry_t *entry,
1396 svn_boolean_t *indirect,
1397 svn_mergeinfo_inheritance_t inherit,
1398 svn_ra_session_t *ra_session,
1399 const char *target_wcpath,
1400 svn_revnum_t start,
1401 svn_revnum_t end,
1402 svn_wc_adm_access_t *adm_access,
1403 svn_client_ctx_t *ctx,
1404 apr_pool_t *pool)
1406 const char *session_url, *url;
1407 svn_revnum_t target_rev;
1408 svn_opt_revision_t peg_revision;
1409 apr_pool_t *sesspool = NULL;
1411 /* Assert that we have sane input. */
1412 assert(SVN_IS_VALID_REVNUM(start)
1413 && SVN_IS_VALID_REVNUM(end)
1414 && (start > end));
1416 /* First, we get the real mergeinfo. */
1417 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(recorded_mergeinfo, entry,
1418 indirect, FALSE, inherit,
1419 ra_session, target_wcpath,
1420 adm_access, ctx, pool));
1422 peg_revision.kind = svn_opt_revision_working;
1423 SVN_ERR(svn_client__derive_location(&url, &target_rev, target_wcpath,
1424 &peg_revision, ra_session, adm_access,
1425 ctx, pool));
1426 if (target_rev <= end)
1428 /* We're asking about a range outside our natural history
1429 altogether. That means our implicit mergeinfo is empty. */
1430 *implicit_mergeinfo = apr_hash_make(pool);
1431 return SVN_NO_ERROR;
1434 /* Temporarily point our RA_SESSION at our target URL so we can
1435 fetch so-called "implicit mergeinfo" (that is, natural history). */
1436 if (ra_session)
1438 SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool));
1439 if (strcmp(url, session_url) != 0)
1440 SVN_ERR(svn_ra_reparent(ra_session, url, pool));
1442 else
1444 sesspool = svn_pool_create(pool);
1445 SVN_ERR(svn_client__open_ra_session_internal(&ra_session, url,
1446 NULL, NULL, NULL, FALSE,
1447 TRUE, ctx, pool));
1450 /* Our underlying APIs can't yet handle the case where the peg
1451 revision isn't the youngest of the three revisions. So we'll
1452 just verify that the source in the peg revision is related to the
1453 the source in the youngest requested revision (which is all the
1454 underlying APIs would do in this case right now anyway). */
1455 if (target_rev < start)
1457 const char *start_url;
1458 svn_opt_revision_t requested, unspec, pegrev, *start_revision;
1459 unspec.kind = svn_opt_revision_unspecified;
1460 requested.kind = svn_opt_revision_number;
1461 requested.value.number = start;
1462 pegrev.kind = svn_opt_revision_number;
1463 pegrev.value.number = target_rev;
1465 SVN_ERR(svn_client__repos_locations(&start_url, &start_revision,
1466 NULL, NULL, ra_session, url,
1467 &pegrev, &requested,
1468 &unspec, ctx, pool));
1469 /* ### FIXME: Having a low-brain moment. Shouldn't we check
1470 that START_URL matches our session URL at this point? */
1471 target_rev = start;
1474 /* Fetch the implicit mergeinfo. */
1475 peg_revision.kind = svn_opt_revision_number;
1476 peg_revision.value.number = target_rev;
1477 SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo, url,
1478 &peg_revision, start, end,
1479 ra_session, NULL, ctx, pool));
1481 /* If we created an RA_SESSION above, destroy it. Otherwise, if
1482 reparented an existing session, point it back where it was when
1483 we were called. */
1484 if (sesspool)
1486 svn_pool_destroy(sesspool);
1488 else if (strcmp(url, session_url) != 0)
1490 SVN_ERR(svn_ra_reparent(ra_session, session_url, pool));
1493 return SVN_NO_ERROR;
1497 /* For each child in CHILDREN_WITH_MERGEINFO, it populates that
1498 child's remaining_ranges list. CHILDREN_WITH_MERGEINFO is expected
1499 to be sorted in depth first order. All persistent allocations are
1500 from CHILDREN_WITH_MERGEINFO->pool.
1502 If HONOR_MERGEINFO is set, this function will actually try to be
1503 intelligent about populating remaining_ranges list. Otherwise, it
1504 will claim that each child has a single remaining range, from
1505 revision1, to revision2.
1507 See `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements
1508 around the values of URL1, REVISION1, URL2, and REVISION2.
1510 static svn_error_t *
1511 populate_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
1512 const char *source_root_url,
1513 const char *url1,
1514 svn_revnum_t revision1,
1515 const char *url2,
1516 svn_revnum_t revision2,
1517 svn_boolean_t inheritable,
1518 svn_boolean_t honor_mergeinfo,
1519 svn_ra_session_t *ra_session,
1520 const char *parent_merge_src_canon_path,
1521 svn_wc_adm_access_t *adm_access,
1522 merge_cmd_baton_t *merge_b)
1524 apr_pool_t *iterpool, *pool;
1525 int merge_target_len = strlen(merge_b->target);
1526 int i;
1528 pool = children_with_mergeinfo->pool;
1529 iterpool = svn_pool_create(pool);
1531 /* If we aren't honoring mergeinfo, we'll make quick work of this by
1532 simply adding dummy REVISION1:REVISION2 ranges for all children. */
1533 if (! honor_mergeinfo)
1535 for (i = 0; i < children_with_mergeinfo->nelts; i++)
1537 svn_client__merge_path_t *child =
1538 APR_ARRAY_IDX(children_with_mergeinfo, i,
1539 svn_client__merge_path_t *);
1540 svn_merge_range_t *range = apr_pcalloc(pool, sizeof(*range));
1542 range->start = revision1;
1543 range->end = revision2;
1544 range->inheritable = inheritable;
1546 child->remaining_ranges =
1547 apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
1548 APR_ARRAY_PUSH(child->remaining_ranges, svn_merge_range_t *) = range;
1550 return SVN_NO_ERROR;
1553 for (i = 0; i < children_with_mergeinfo->nelts; i++)
1555 const char *child_repos_path;
1556 const svn_wc_entry_t *child_entry;
1557 const char *child_url1, *child_url2;
1558 apr_hash_t *implicit_mergeinfo;
1559 svn_client__merge_path_t *child =
1560 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
1562 /* If the path is absent don't do subtree merge either. */
1563 if (!child || child->absent)
1564 continue;
1566 svn_pool_clear(iterpool);
1568 if (strlen(child->path) == merge_target_len)
1569 child_repos_path = "";
1570 else
1571 child_repos_path = child->path +
1572 (merge_target_len ? merge_target_len + 1 : 0);
1573 child_url1 = svn_path_join(url1, child_repos_path, iterpool);
1574 child_url2 = svn_path_join(url2, child_repos_path, iterpool);
1576 SVN_ERR(svn_wc__entry_versioned(&child_entry, child->path, adm_access,
1577 FALSE, iterpool));
1579 SVN_ERR(get_full_mergeinfo(&(child->pre_merge_mergeinfo),
1580 &implicit_mergeinfo, child_entry,
1581 &(child->indirect_mergeinfo),
1582 svn_mergeinfo_inherited, NULL, child->path,
1583 MAX(revision1, revision2),
1584 MIN(revision1, revision2),
1585 adm_access, merge_b->ctx, pool));
1587 SVN_ERR(calculate_remaining_ranges(&(child->remaining_ranges),
1588 source_root_url,
1589 child_url1, revision1,
1590 child_url2, revision2,
1591 inheritable,
1592 child->pre_merge_mergeinfo,
1593 implicit_mergeinfo,
1594 ra_session, child_entry, merge_b->ctx,
1595 pool));
1598 /* Take advantage of the depth first ordering,
1599 i.e first(0th) item is target.*/
1600 if (children_with_mergeinfo->nelts > 1)
1602 svn_client__merge_path_t *child =
1603 APR_ARRAY_IDX(children_with_mergeinfo, 0, svn_client__merge_path_t *);
1605 if (child->remaining_ranges->nelts == 0)
1607 svn_merge_range_t *dummy_range =
1608 apr_pcalloc(pool, sizeof(*dummy_range));
1609 dummy_range->start = revision2;
1610 dummy_range->end = revision2;
1611 dummy_range->inheritable = inheritable;
1612 child->remaining_ranges = apr_array_make(pool, 1,
1613 sizeof(dummy_range));
1614 APR_ARRAY_PUSH(child->remaining_ranges, svn_merge_range_t *) =
1615 dummy_range;
1616 merge_b->target_has_dummy_merge_range = TRUE;
1620 svn_pool_destroy(iterpool);
1621 return SVN_NO_ERROR;
1625 /*-----------------------------------------------------------------------*/
1627 /*** Compacting Merge Ranges ***/
1630 /* Callback for qsort() calls which need to sort svn_merge_range_t *.
1631 Wraps svn_sort_compare_ranges() but first "normalizes" all ranges
1632 so range->end > range->start. */
1633 static int
1634 compare_merge_ranges(const void *a,
1635 const void *b)
1637 /* ### Are all these pointer gymnastics necessary?
1638 ### There's gotta be a simpler way... */
1639 svn_merge_range_t *s1 = *((svn_merge_range_t * const *) a);
1640 svn_merge_range_t *s2 = *((svn_merge_range_t * const *) b);
1642 /* Convert the svn_merge_range_t to svn_merge_range_t and leverage our
1643 existing comparison function. */
1644 svn_merge_range_t r1 = { MIN(s1->start, s1->end),
1645 MAX(s1->start, s1->end),
1646 TRUE};
1647 svn_merge_range_t r2 = { MIN(s2->start, s2->end),
1648 MAX(s2->start, s2->end),
1649 TRUE};
1650 svn_merge_range_t *r1p = &r1;
1651 svn_merge_range_t *r2p = &r2;
1652 return svn_sort_compare_ranges(&r1p, &r2p);
1655 /* Another qsort() callback. Wraps compare_merge_ranges(), but only
1656 for ranges that share a common "direction", e.g. additive or
1657 subtractive ranges. If both ranges are subtractive, the range with
1658 the lowest (highest absolute) range value is consider the lesser.
1659 If the direction is not the same, then consider additive merges to
1660 always be less than subtractive merges. */
1661 static int
1662 compare_merge_ranges2(const void *a,
1663 const void *b)
1665 svn_merge_range_t *s1 = *((svn_merge_range_t * const *) a);
1666 svn_merge_range_t *s2 = *((svn_merge_range_t * const *) b);
1667 svn_boolean_t s1_reversed = s1->start > s1->end;
1668 svn_boolean_t s2_reversed = s2->start > s2->end;
1670 if (s1_reversed && s2_reversed)
1671 return -(compare_merge_ranges(a, b));
1672 else if (s1_reversed)
1673 return 1;
1674 else if (s2_reversed)
1675 return -1;
1676 else
1677 return compare_merge_ranges(a, b);
1680 /* Helper for compact_merge_ranges. Take *RANGES, an array of
1681 svn_merge_range_t *, and and remove any redundant ranges, possibly
1682 removing items from *RANGES. *RANGES must be sorted per
1683 compare_merge_ranges() and is guaranteed to be sorted thusly
1684 upon completion. All range in RANGES must also be of the same
1685 "direction" (additive or subtractive). */
1686 static void
1687 remove_redundant_ranges(apr_array_header_t **ranges)
1689 svn_merge_range_t *range_1 = NULL;
1690 svn_merge_range_t *range_2;
1691 int i;
1693 for (i = 0; i < (*ranges)->nelts; i++)
1695 if (range_1 == NULL)
1697 range_1 = APR_ARRAY_IDX(*ranges, i, svn_merge_range_t *);
1698 continue;
1700 else
1702 range_2 = APR_ARRAY_IDX(*ranges, i, svn_merge_range_t *);
1704 if (svn_range_compact(&range_1, &range_2))
1706 if (!range_2)
1708 /* Able to compact the two ranges into one.
1709 Remove merge_ranges[i] and from array. */
1710 int j;
1711 for (j = i; j < (*ranges)->nelts - 1; j++)
1713 APR_ARRAY_IDX(*ranges, j, svn_merge_range_t *) =
1714 APR_ARRAY_IDX(*ranges, j + 1, svn_merge_range_t *);
1716 apr_array_pop(*ranges);
1717 i--; /* Reprocess this element */
1723 /* Helper for compact_merge_ranges. SOURCES is array of svn_merge_range_t *
1724 sorted per compare_merge_ranges(). Remove any redundant ranges between
1725 adjacent ranges and store the result in *COMPACTED_RANGES, allocated out
1726 of pool. The ranges in *COMPACTED_RANGES will remain sorted as per
1727 compare_merge_ranges. Range in RANGES can be of either direction
1728 (additive and/or subtractive). */
1729 static void
1730 compact_add_sub_ranges(apr_array_header_t **compacted_sources,
1731 apr_array_header_t *sources,
1732 apr_pool_t *pool)
1734 int i;
1735 svn_merge_range_t *range_1 = NULL;
1736 svn_merge_range_t *range_2;
1737 apr_array_header_t *merge_ranges = apr_array_copy(pool, sources);
1739 for (i = 0; i < merge_ranges->nelts; i++)
1741 if (range_1 == NULL)
1743 range_1 = APR_ARRAY_IDX(merge_ranges, i, svn_merge_range_t *);
1744 continue;
1746 else
1748 range_2 = APR_ARRAY_IDX(merge_ranges, i, svn_merge_range_t *);
1751 if (svn_range_compact(&range_1, &range_2))
1753 if (range_1 == NULL && range_2 == NULL) /* ranges cancel each out */
1755 /* Remove merge_ranges[i] and merge_ranges[i + 1]
1756 from the array. */
1757 int j;
1758 for (j = i - 1; j < merge_ranges->nelts - 2; j++)
1760 APR_ARRAY_IDX(merge_ranges, j, svn_merge_range_t *) =
1761 APR_ARRAY_IDX(merge_ranges, j + 2, svn_merge_range_t *);
1763 apr_array_pop(merge_ranges);
1764 apr_array_pop(merge_ranges);
1765 /* Make range_1 the last range processed if one exists. */
1766 if (i > 1)
1767 range_1 = APR_ARRAY_IDX(merge_ranges, i - 2,
1768 svn_merge_range_t *);
1770 else if (!range_2) /* ranges compacted into range_ 1 */
1772 /* Remove merge_ranges[i] and from array. */
1773 int j;
1774 for (j = i; j < merge_ranges->nelts - 1; j++)
1776 APR_ARRAY_IDX(merge_ranges, j, svn_merge_range_t *) =
1777 APR_ARRAY_IDX(merge_ranges, j + 1, svn_merge_range_t *);
1779 apr_array_pop(merge_ranges);
1781 i--; /* Reprocess merge_ranges[i] */
1783 else /* ranges compacted */
1785 range_1 = range_2;
1787 } /* if (svn_range_compact(&range_1, &range_2)) */
1791 *compacted_sources = merge_ranges;
1794 /* SOURCES is array of svn_merge_range_t *sorted per compare_merge_ranges(). */
1795 static svn_error_t *
1796 compact_merge_ranges(apr_array_header_t **compacted_sources_p,
1797 apr_array_header_t *merge_ranges,
1798 apr_pool_t *pool)
1800 apr_array_header_t *additive_sources =
1801 apr_array_make(pool, 0, sizeof(svn_merge_range_t *));
1802 apr_array_header_t *subtractive_sources =
1803 apr_array_make(pool, 0, sizeof(svn_merge_range_t *));
1804 apr_array_header_t *compacted_sources;
1805 int i;
1807 for (i = 0; i < merge_ranges->nelts; i++)
1809 svn_merge_range_t *range =
1810 svn_merge_range_dup(APR_ARRAY_IDX(merge_ranges, i,
1811 svn_merge_range_t *), pool);
1812 if (range->start > range->end)
1813 APR_ARRAY_PUSH(subtractive_sources, svn_merge_range_t *) = range;
1814 else
1815 APR_ARRAY_PUSH(additive_sources, svn_merge_range_t *) = range;
1818 qsort(additive_sources->elts, additive_sources->nelts,
1819 additive_sources->elt_size, compare_merge_ranges);
1820 remove_redundant_ranges(&additive_sources);
1821 qsort(subtractive_sources->elts, subtractive_sources->nelts,
1822 subtractive_sources->elt_size, compare_merge_ranges);
1823 remove_redundant_ranges(&subtractive_sources);
1825 for (i = 0; i < subtractive_sources->nelts; i++)
1827 svn_merge_range_t *range =
1828 svn_merge_range_dup(APR_ARRAY_IDX(subtractive_sources, i,
1829 svn_merge_range_t *), pool);
1830 APR_ARRAY_PUSH(additive_sources, svn_merge_range_t *) = range;
1833 qsort(additive_sources->elts, additive_sources->nelts,
1834 additive_sources->elt_size, compare_merge_ranges);
1835 compact_add_sub_ranges(&compacted_sources, additive_sources, pool);
1836 qsort(compacted_sources->elts, compacted_sources->nelts,
1837 compacted_sources->elt_size, compare_merge_ranges2);
1838 *compacted_sources_p = compacted_sources;
1839 return SVN_NO_ERROR;
1842 /*-----------------------------------------------------------------------*/
1844 /*** Other Helper Functions ***/
1847 /* Create mergeinfo describing the merge of RANGE into our target, accounting
1848 for paths unaffected by the merge due to skips or conflicts from
1849 NOTIFY_B. For 'immediates' merge it sets an inheritable mergeinfo
1850 corresponding to current merge on merge target. For 'files' merge it sets
1851 an inheritable mergeinfo corrsponding to current merge on merged files.
1852 Note in MERGE_B->OPERATIVE_MERGE if an operative merge
1853 is discovered. If TARGET_WCPATH is a directory and it is missing
1854 an immediate child then TARGET_MISSING_CHILD should be true,
1855 otherwise it is false.*/
1856 static svn_error_t *
1857 determine_merges_performed(apr_hash_t **merges, const char *target_wcpath,
1858 svn_merge_range_t *range,
1859 svn_depth_t depth,
1860 svn_wc_adm_access_t *adm_access,
1861 notification_receiver_baton_t *notify_b,
1862 merge_cmd_baton_t *merge_b,
1863 apr_pool_t *pool)
1865 apr_array_header_t *rangelist;
1866 apr_size_t nbr_skips = (notify_b->skipped_paths != NULL ?
1867 apr_hash_count(notify_b->skipped_paths) : 0);
1868 *merges = apr_hash_make(pool);
1870 /* If there have been no operative merges, then don't calculate anything.
1871 Just return the empty hash because this whole merge has been a no-op
1872 and we don't change the mergeinfo in that case (issue #2883). */
1873 if (notify_b->nbr_operative_notifications > 0)
1874 merge_b->operative_merge = TRUE;
1875 else
1876 return SVN_NO_ERROR;
1878 rangelist = apr_array_make(pool, 1, sizeof(range));
1879 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = range;
1880 apr_hash_set(*merges, target_wcpath, APR_HASH_KEY_STRING, rangelist);
1881 if (nbr_skips > 0)
1883 apr_hash_index_t *hi;
1885 /* Override the mergeinfo for child paths which weren't
1886 actually merged. */
1887 for (hi = apr_hash_first(NULL, notify_b->skipped_paths); hi;
1888 hi = apr_hash_next(hi))
1890 const void *skipped_path;
1891 apr_hash_this(hi, &skipped_path, NULL, NULL);
1893 /* Add an empty range list for this path. */
1894 apr_hash_set(*merges, (const char *) skipped_path,
1895 APR_HASH_KEY_STRING,
1896 apr_array_make(pool, 0, sizeof(range)));
1898 if (nbr_skips < notify_b->nbr_notifications)
1899 /* ### Use RANGELIST as the mergeinfo for all children of
1900 ### this path which were not also explicitly
1901 ### skipped? */
1905 if ((depth != svn_depth_infinity) && notify_b->merged_paths)
1907 apr_hash_index_t *hi;
1908 const void *merged_path;
1910 for (hi = apr_hash_first(NULL, notify_b->merged_paths); hi;
1911 hi = apr_hash_next(hi))
1913 const svn_wc_entry_t *child_entry;
1914 svn_merge_range_t *child_merge_range;
1915 apr_array_header_t *rangelist_of_child = NULL;
1916 apr_hash_this(hi, &merged_path, NULL, NULL);
1917 child_merge_range = svn_merge_range_dup(range, pool);
1918 SVN_ERR(svn_wc__entry_versioned(&child_entry,
1919 merged_path,
1920 adm_access, FALSE,
1921 pool));
1922 if (((child_entry->kind == svn_node_dir)
1923 && (strcmp(merge_b->target, merged_path) == 0)
1924 && (depth == svn_depth_immediates))
1925 || ((child_entry->kind == svn_node_file)
1926 && (depth == svn_depth_files)))
1928 /* Set the explicit inheritable mergeinfo for,
1929 1. Merge target directory if depth is immediates.
1930 2. If merge is on a file and requested depth is 'files'.
1932 child_merge_range->inheritable = TRUE;
1933 rangelist_of_child = apr_array_make(pool, 1, sizeof(range));
1935 if (rangelist_of_child)
1937 APR_ARRAY_PUSH(rangelist_of_child, svn_merge_range_t *) =
1938 child_merge_range;
1940 apr_hash_set(*merges, (const char *)merged_path,
1941 APR_HASH_KEY_STRING, rangelist_of_child);
1946 return SVN_NO_ERROR;
1949 /* Calculate the new mergeinfo for the target tree based on the merge
1950 info for TARGET_WCPATH and MERGES (a mapping of WC paths to range
1951 lists), and record it in the WC (at, and possibly below,
1952 TARGET_WCPATH). */
1953 static svn_error_t *
1954 update_wc_mergeinfo(const char *target_wcpath, const svn_wc_entry_t *entry,
1955 const char *repos_rel_path, apr_hash_t *merges,
1956 svn_boolean_t is_rollback,
1957 svn_wc_adm_access_t *adm_access,
1958 svn_client_ctx_t *ctx, apr_pool_t *pool)
1960 apr_pool_t *subpool = svn_pool_create(pool);
1961 const char *rel_path;
1962 apr_hash_t *mergeinfo;
1963 apr_hash_index_t *hi;
1965 /* Combine the mergeinfo for the revision range just merged into
1966 the WC with its on-disk mergeinfo. */
1967 for (hi = apr_hash_first(pool, merges); hi; hi = apr_hash_next(hi))
1969 const void *key;
1970 void *value;
1971 const char *path;
1972 apr_array_header_t *ranges, *rangelist;
1973 int len;
1974 svn_error_t *err;
1976 svn_pool_clear(subpool);
1978 apr_hash_this(hi, &key, NULL, &value);
1979 path = key;
1980 ranges = value;
1982 /* As some of the merges may've changed the WC's mergeinfo, get
1983 a fresh copy before using it to update the WC's mergeinfo. */
1984 SVN_ERR(svn_client__parse_mergeinfo(&mergeinfo, entry, path, FALSE,
1985 adm_access, ctx, subpool));
1987 /* If we are attempting to set empty revision range override mergeinfo
1988 on a path with no explicit mergeinfo, we first need the pristine
1989 mergeinfo that path inherits. */
1990 if (mergeinfo == NULL && ranges->nelts == 0)
1992 svn_boolean_t inherited;
1993 SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, &inherited, TRUE,
1994 svn_mergeinfo_nearest_ancestor,
1995 entry, path, NULL, NULL,
1996 adm_access, ctx, subpool));
1999 if (mergeinfo == NULL)
2000 mergeinfo = apr_hash_make(subpool);
2002 /* ASSUMPTION: "target_wcpath" is always both a parent and
2003 prefix of "path". */
2004 len = strlen(target_wcpath);
2005 if (len < strlen(path))
2007 const char *path_relative_to_target = len?(path + len + 1):(path);
2008 rel_path = apr_pstrcat(subpool, repos_rel_path, "/",
2009 path_relative_to_target, NULL);
2011 else
2012 rel_path = repos_rel_path;
2013 rangelist = apr_hash_get(mergeinfo, rel_path, APR_HASH_KEY_STRING);
2014 if (rangelist == NULL)
2015 rangelist = apr_array_make(subpool, 0, sizeof(svn_merge_range_t *));
2017 if (is_rollback)
2019 ranges = svn_rangelist_dup(ranges, subpool);
2020 SVN_ERR(svn_rangelist_reverse(ranges, subpool));
2021 SVN_ERR(svn_rangelist_remove(&rangelist, ranges, rangelist,
2022 FALSE,
2023 subpool));
2025 else
2027 SVN_ERR(svn_rangelist_merge(&rangelist, ranges,
2028 subpool));
2030 /* Update the mergeinfo by adjusting the path's rangelist. */
2031 apr_hash_set(mergeinfo, rel_path, APR_HASH_KEY_STRING, rangelist);
2033 if (is_rollback && apr_hash_count(mergeinfo) == 0)
2034 mergeinfo = NULL;
2036 svn_mergeinfo__remove_empty_rangelists(mergeinfo, pool);
2038 err = svn_client__record_wc_mergeinfo(path, mergeinfo,
2039 adm_access, subpool);
2041 if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
2043 /* PATH isn't just missing, it's not even versioned as far
2044 as this working copy knows. But it was included in
2045 MERGES, which means that the server knows about it.
2046 Likely we don't have access to the source due to authz
2047 restrictions. For now just clear the error and
2048 continue...
2050 ### TODO: Set non-inheritable mergeinfo on PATH's immediate
2051 ### parent and normal mergeinfo on PATH's siblings which we
2052 ### do have access to. */
2053 svn_error_clear(err);
2055 else
2056 SVN_ERR(err);
2059 svn_pool_destroy(subpool);
2060 return SVN_NO_ERROR;
2064 /* Create and return an error structure appropriate for the unmerged
2065 revisions range(s). */
2066 static APR_INLINE svn_error_t *
2067 make_merge_conflict_error(const char *target_wcpath,
2068 svn_merge_range_t *r,
2069 apr_pool_t *pool)
2071 return svn_error_createf
2072 (SVN_ERR_WC_FOUND_CONFLICT, NULL,
2073 _("One or more conflicts were produced while merging r%ld:%ld into\n"
2074 "'%s' --\n"
2075 "resolve all conflicts and rerun the merge to apply the remaining\n"
2076 "unmerged revisions"),
2077 r->start, r->end, svn_path_local_style(target_wcpath, pool));
2080 /* Helper for do_directory_merge().
2082 TARGET_WCPATH is a directory and CHILDREN_WITH_MERGEINFO is filled
2083 with paths (svn_client__merge_path_t *) arranged in depth first order,
2084 which have mergeinfo set on them or meet one of the other criteria
2085 defined in get_mergeinfo_paths(). Remove any paths absent from disk
2086 or scheduled for deletion from CHILDREN_WITH_MERGEINFO which are equal to
2087 or are descendants of TARGET_WCPATH by setting those children to NULL.
2088 Also remove the path from the NOTIFY_B->SKIPPED_PATHS hash. */
2089 static void
2090 remove_absent_children(const char *target_wcpath,
2091 apr_array_header_t *children_with_mergeinfo,
2092 notification_receiver_baton_t *notify_b)
2094 /* Before we try to override mergeinfo for skipped paths, make sure
2095 the path isn't absent due to authz restrictions, because there's
2096 nothing we can do about those. */
2097 int i;
2098 for (i = 0; i < children_with_mergeinfo->nelts; i++)
2100 svn_client__merge_path_t *child =
2101 APR_ARRAY_IDX(children_with_mergeinfo,
2102 i, svn_client__merge_path_t *);
2103 if (child
2104 && (child->absent || child->scheduled_for_deletion)
2105 && svn_path_is_ancestor(target_wcpath, child->path))
2107 if (notify_b->skipped_paths)
2108 apr_hash_set(notify_b->skipped_paths, child->path,
2109 APR_HASH_KEY_STRING, NULL);
2110 APR_ARRAY_IDX(children_with_mergeinfo, i,
2111 svn_client__merge_path_t *) = NULL;
2116 /* Set *HONOR_MERGEINFO and *RECORD_MERGEINFO (if non-NULL)
2117 appropriately for MERGE_B. */
2118 static APR_INLINE void
2119 mergeinfo_behavior(svn_boolean_t *honor_mergeinfo,
2120 svn_boolean_t *record_mergeinfo,
2121 merge_cmd_baton_t *merge_b)
2123 if (honor_mergeinfo)
2124 *honor_mergeinfo = (merge_b->mergeinfo_capable
2125 && merge_b->sources_ancestral
2126 && merge_b->same_repos
2127 && (! merge_b->ignore_ancestry));
2129 if (record_mergeinfo)
2130 *record_mergeinfo = (merge_b->mergeinfo_capable
2131 && merge_b->sources_ancestral
2132 && merge_b->same_repos
2133 && (! merge_b->dry_run));
2136 /* Sets up the diff editor report and drives it by properly negating
2137 subtree that could have a conflicting merge history.
2139 MERGE_B->ra_session1 reflects URL1; MERGE_B->ra_session2 reflects URL2.
2141 If MERGE_B->sources_ancestral is set, then URL1@REVISION1 must be a
2142 historical ancestor of URL2@REVISION2, or vice-versa (see
2143 `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements around
2144 the values of URL1, REVISION1, URL2, and REVISION2 in this case).
2146 static svn_error_t *
2147 drive_merge_report_editor(const char *target_wcpath,
2148 const char *url1,
2149 svn_revnum_t revision1,
2150 const char *url2,
2151 svn_revnum_t revision2,
2152 apr_array_header_t *children_with_mergeinfo,
2153 svn_boolean_t is_rollback,
2154 svn_depth_t depth,
2155 notification_receiver_baton_t *notify_b,
2156 svn_wc_adm_access_t *adm_access,
2157 const svn_wc_diff_callbacks2_t *callbacks,
2158 merge_cmd_baton_t *merge_b,
2159 apr_pool_t *pool)
2161 const svn_ra_reporter3_t *reporter;
2162 const svn_delta_editor_t *diff_editor;
2163 void *diff_edit_baton;
2164 void *report_baton;
2165 svn_revnum_t default_start;
2166 svn_boolean_t honor_mergeinfo;
2167 const char *old_sess2_url;
2169 mergeinfo_behavior(&honor_mergeinfo, NULL, merge_b);
2171 /* Calculate the default starting revision. */
2172 default_start = revision1;
2173 if (honor_mergeinfo)
2175 if (merge_b->target_has_dummy_merge_range)
2177 default_start = revision2;
2179 else if (children_with_mergeinfo && children_with_mergeinfo->nelts)
2181 svn_client__merge_path_t *child =
2182 APR_ARRAY_IDX(children_with_mergeinfo, 0,
2183 svn_client__merge_path_t *);
2184 if (child->remaining_ranges->nelts)
2186 svn_merge_range_t *range =
2187 APR_ARRAY_IDX(child->remaining_ranges, 0,
2188 svn_merge_range_t *);
2189 default_start = range->start;
2194 /* Temporarily point our second RA session to URL1, too. We use
2195 this to request individual file contents. */
2196 SVN_ERR(svn_client__ensure_ra_session_url(&old_sess2_url,
2197 merge_b->ra_session2, url1, pool));
2199 /* Get the diff editor and a reporter with which to, ultimately,
2200 drive it. */
2201 SVN_ERR(svn_client__get_diff_editor(target_wcpath, adm_access, callbacks,
2202 merge_b, depth, merge_b->dry_run,
2203 merge_b->ra_session2, default_start,
2204 notification_receiver, notify_b,
2205 merge_b->ctx->cancel_func,
2206 merge_b->ctx->cancel_baton,
2207 &diff_editor, &diff_edit_baton,
2208 pool));
2209 SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1,
2210 &reporter, &report_baton, revision2,
2211 "", depth, merge_b->ignore_ancestry,
2212 TRUE, /* text_deltas */
2213 url2, diff_editor, diff_edit_baton, pool));
2215 /* Drive the reporter. */
2216 SVN_ERR(reporter->set_path(report_baton, "", default_start, depth,
2217 FALSE, NULL, pool));
2218 if (honor_mergeinfo && children_with_mergeinfo)
2220 /* Describe children with mergeinfo overlapping this merge
2221 operation such that no repeated diff is retrieved for them from
2222 the repository. */
2223 apr_size_t target_wcpath_len = strlen(target_wcpath);
2224 int i;
2226 for (i = 1; i < children_with_mergeinfo->nelts; i++)
2228 svn_merge_range_t *range;
2229 const char *child_repos_path;
2230 svn_client__merge_path_t *child =
2231 APR_ARRAY_IDX(children_with_mergeinfo, i,
2232 svn_client__merge_path_t *);
2234 if (!child || child->absent || (child->remaining_ranges->nelts == 0))
2235 continue;
2237 range = APR_ARRAY_IDX(child->remaining_ranges, 0,
2238 svn_merge_range_t *);
2239 if (range->start == default_start)
2240 continue;
2242 child_repos_path = child->path +
2243 (target_wcpath_len ? target_wcpath_len + 1 : 0);
2245 if ((is_rollback && (range->start < revision2))
2246 || (!is_rollback && (range->start > revision2)))
2248 SVN_ERR(reporter->set_path(report_baton, child_repos_path,
2249 revision2, depth, FALSE, NULL, pool));
2251 else
2253 SVN_ERR(reporter->set_path(report_baton, child_repos_path,
2254 range->start, depth, FALSE,
2255 NULL, pool));
2259 SVN_ERR(reporter->finish_report(report_baton, pool));
2261 /* Point the merge baton's second session back where it was. */
2262 if (old_sess2_url)
2263 SVN_ERR(svn_ra_reparent(merge_b->ra_session2, old_sess2_url, pool));
2265 /* Sleep to ensure timestamp integrity. */
2266 svn_sleep_for_timestamps();
2268 return SVN_NO_ERROR;
2271 /* Return the most inclusive range start revision across all the
2272 remaining ranges in CHILDREN_WITH_MERGEINFO. If there are no
2273 remaining ranges, return SVN_INVALID_REVNUM. Skip no-op ranges
2274 on the target (they are probably dummies). */
2275 static svn_revnum_t
2276 get_most_inclusive_start_rev(apr_array_header_t *children_with_mergeinfo,
2277 svn_boolean_t is_rollback)
2279 int i;
2280 svn_revnum_t start_rev = SVN_INVALID_REVNUM;
2282 for (i = 0; i < children_with_mergeinfo->nelts; i++)
2284 svn_client__merge_path_t *child =
2285 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
2286 svn_merge_range_t *range;
2288 if ((! child) || child->absent)
2289 continue;
2290 if (! child->remaining_ranges->nelts)
2291 continue;
2292 range = APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
2293 if ((i == 0) && (range->start == range->end))
2294 continue;
2295 if ((start_rev == SVN_INVALID_REVNUM)
2296 || (is_rollback && (range->start > start_rev))
2297 || ((! is_rollback) && (range->start < start_rev)))
2298 start_rev = range->start;
2300 return start_rev;
2303 /* Return the youngest qualifying end revision across the first of
2304 each child in CHILDREN_WITH_MERGEINFO's remaining ranges. If
2305 nothing qualifies, return SVN_INVALID_REVNUM. */
2306 static svn_revnum_t
2307 get_youngest_end_rev(apr_array_header_t *children_with_mergeinfo,
2308 svn_boolean_t is_rollback)
2310 int i;
2311 svn_revnum_t end_rev = SVN_INVALID_REVNUM;
2313 for (i = 0; i < children_with_mergeinfo->nelts; i++)
2315 svn_client__merge_path_t *child =
2316 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
2317 if (!child || child->absent)
2318 continue;
2319 if (child->remaining_ranges->nelts > 0)
2321 svn_merge_range_t *range = APR_ARRAY_IDX(child->remaining_ranges, 0,
2322 svn_merge_range_t *);
2323 if ((end_rev == SVN_INVALID_REVNUM)
2324 || (is_rollback && (range->end > end_rev))
2325 || ((! is_rollback) && (range->end < end_rev)))
2326 end_rev = range->end;
2329 return end_rev;
2332 /* If first item in each child of CHILDREN_WITH_MERGEINFO's
2333 remaining_ranges is inclusive of END_REV, Slice the first range in
2334 to two at END_REV. All the allocations are persistent and allocated
2335 from POOL. */
2336 static void
2337 slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
2338 svn_boolean_t is_rollback, svn_revnum_t end_rev,
2339 apr_pool_t *pool)
2341 int i;
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,
2346 svn_client__merge_path_t *);
2347 if (!child || child->absent)
2348 continue;
2349 if (child->remaining_ranges->nelts > 0)
2351 svn_merge_range_t *range = APR_ARRAY_IDX(child->remaining_ranges, 0,
2352 svn_merge_range_t *);
2353 if ((is_rollback && (range->start > end_rev)
2354 && (range->end < end_rev))
2355 || (!is_rollback && (range->start < end_rev)
2356 && (range->end > end_rev)))
2358 int j;
2359 svn_merge_range_t *split_range1, *split_range2;
2360 apr_array_header_t *orig_remaining_ranges =
2361 child->remaining_ranges;
2362 split_range1 = svn_merge_range_dup(range, pool);
2363 split_range2 = svn_merge_range_dup(range, pool);
2364 split_range1->end = end_rev;
2365 split_range2->start = end_rev;
2366 child->remaining_ranges =
2367 apr_array_make(pool, (child->remaining_ranges->nelts + 1),
2368 sizeof(svn_merge_range_t *));
2369 APR_ARRAY_PUSH(child->remaining_ranges,
2370 svn_merge_range_t *) = split_range1;
2371 APR_ARRAY_PUSH(child->remaining_ranges,
2372 svn_merge_range_t *) = split_range2;
2373 for (j = 1; j < orig_remaining_ranges->nelts; j++)
2375 svn_merge_range_t *orig_range =
2376 APR_ARRAY_IDX(orig_remaining_ranges, j,
2377 svn_merge_range_t *);
2378 APR_ARRAY_PUSH(child->remaining_ranges,
2379 svn_merge_range_t *) = orig_range;
2386 /* For each child of CHILDREN_WITH_MERGEINFO create a new remaining_ranges
2387 by removing the first item from the original range list and overwrite the
2388 original remaining_ranges with this new list.
2389 All the allocations are persistent from a POOL.
2390 TODO, we should have remaining_ranges in reverse order to avoid recreating
2391 the remaining_ranges every time instead of one 'pop' operation. */
2392 static void
2393 remove_first_range_from_remaining_ranges(
2394 apr_array_header_t *children_with_mergeinfo,
2395 apr_pool_t *pool)
2397 int i, j;
2398 for (i = 0; i < children_with_mergeinfo->nelts; i++)
2400 svn_client__merge_path_t *child =
2401 APR_ARRAY_IDX(children_with_mergeinfo, i,
2402 svn_client__merge_path_t *);
2403 if (!child || child->absent)
2404 continue;
2405 if (child->remaining_ranges->nelts > 0)
2407 apr_array_header_t *orig_remaining_ranges = child->remaining_ranges;
2408 child->remaining_ranges =
2409 apr_array_make(pool, (child->remaining_ranges->nelts - 1),
2410 sizeof(svn_merge_range_t *));
2411 for (j = 1; j < orig_remaining_ranges->nelts; j++)
2413 svn_merge_range_t *range = APR_ARRAY_IDX(orig_remaining_ranges,
2415 svn_merge_range_t *);
2416 APR_ARRAY_PUSH(child->remaining_ranges, svn_merge_range_t *)
2417 = range;
2424 /* Blindly record the range specified by the user (rather than refining it
2425 as we do for actual merges) for the merge source URL. */
2426 static svn_error_t *
2427 record_mergeinfo_for_record_only_merge(const char *url,
2428 svn_merge_range_t *range,
2429 const svn_wc_entry_t *entry,
2430 svn_wc_adm_access_t *adm_access,
2431 merge_cmd_baton_t *merge_b,
2432 apr_pool_t *pool)
2434 apr_array_header_t *rangelist;
2435 const char *rel_path;
2436 apr_hash_t *target_mergeinfo;
2437 svn_boolean_t indirect;
2438 apr_hash_t *merges = apr_hash_make(pool);
2439 svn_boolean_t is_rollback = (range->start > range->end);
2441 /* Temporarily reparent ra_session to WC target URL. */
2442 SVN_ERR(svn_ra_reparent(merge_b->ra_session1, entry->url, pool));
2443 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(&target_mergeinfo, entry,
2444 &indirect, FALSE,
2445 svn_mergeinfo_inherited,
2446 merge_b->ra_session1,
2447 merge_b->target,
2448 adm_access, merge_b->ctx,
2449 pool));
2450 /* Reparent ra_session back to URL. */
2451 SVN_ERR(svn_ra_reparent(merge_b->ra_session1, url, pool));
2452 SVN_ERR(svn_client__path_relative_to_root(&rel_path, url, NULL, TRUE,
2453 merge_b->ra_session1,
2454 adm_access, pool));
2455 rangelist = apr_array_make(pool, 1, sizeof(range));
2456 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = range;
2457 apr_hash_set(merges, merge_b->target, APR_HASH_KEY_STRING, rangelist);
2459 /* If merge target has indirect mergeinfo set it. */
2460 if (indirect)
2461 SVN_ERR(svn_client__record_wc_mergeinfo(merge_b->target, target_mergeinfo,
2462 adm_access, pool));
2464 return update_wc_mergeinfo(merge_b->target, entry, rel_path, merges,
2465 is_rollback, adm_access, merge_b->ctx, pool);
2468 /* Marks 'inheritable' RANGE to TARGET_WCPATH by wiping off the
2469 corresponding 'non-inheritable' RANGE from TARGET_MERGEINFO for the
2470 merge source REL_PATH. It does such marking only for same URLs
2471 from same Repository, not a dry run, target having existing
2472 mergeinfo(TARGET_MERGEINFO) and target being part of
2473 CHILDREN_WITH_MERGEINFO. */
2474 static svn_error_t *
2475 mark_mergeinfo_as_inheritable_for_a_range(
2476 apr_hash_t *target_mergeinfo,
2477 svn_boolean_t same_urls,
2478 svn_merge_range_t *range,
2479 const char *rel_path,
2480 const char *target_wcpath,
2481 svn_wc_adm_access_t *adm_access,
2482 merge_cmd_baton_t *merge_b,
2483 apr_array_header_t *children_with_mergeinfo,
2484 int target_index, apr_pool_t *pool)
2486 /* Check if we need to make non-inheritable ranges inheritable. */
2487 if (target_mergeinfo && same_urls
2488 && !merge_b->dry_run
2489 && merge_b->same_repos
2490 && target_index >= 0)
2492 svn_client__merge_path_t *merge_path =
2493 APR_ARRAY_IDX(children_with_mergeinfo,
2494 target_index, svn_client__merge_path_t *);
2496 /* If a path has no missing children, has non-inheritable ranges,
2497 *and* those non-inheritable ranges intersect with the merge being
2498 performed (i.e. this is a repeat merge where a previously missing
2499 child is now present) then those non-inheritable ranges are made
2500 inheritable. */
2501 if (merge_path
2502 && merge_path->has_noninheritable && !merge_path->missing_child)
2504 svn_boolean_t is_equal;
2505 apr_hash_t *merges;
2506 apr_hash_t *inheritable_merges = apr_hash_make(pool);
2507 apr_array_header_t *inheritable_ranges =
2508 apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
2510 APR_ARRAY_PUSH(inheritable_ranges, svn_merge_range_t *) = range;
2511 apr_hash_set(inheritable_merges, rel_path, APR_HASH_KEY_STRING,
2512 inheritable_ranges);
2514 /* Try to remove any non-inheritable ranges bound by the merge
2515 being performed. */
2516 SVN_ERR(svn_mergeinfo_inheritable(&merges, target_mergeinfo,
2517 rel_path, range->start,
2518 range->end, pool));
2519 /* If any non-inheritable ranges were removed put them back as
2520 inheritable ranges. */
2521 SVN_ERR(svn_mergeinfo__equals(&is_equal, merges, target_mergeinfo,
2522 FALSE, pool));
2523 if (!is_equal)
2525 SVN_ERR(svn_mergeinfo_merge(merges, inheritable_merges, pool));
2526 SVN_ERR(svn_client__record_wc_mergeinfo(target_wcpath, merges,
2527 adm_access, pool));
2531 return SVN_NO_ERROR;
2534 /* For shallow merges record the explicit *indirect* mergeinfo on the
2536 1. merged files *merged* with a depth 'files'.
2537 2. merged target directory *merged* with a depth 'immediates'.
2539 All subtrees which are going to get a 'inheritable merge range'
2540 because of this 'shallow' merge should have the explicit mergeinfo
2541 recorded on them. */
2542 static svn_error_t *
2543 record_mergeinfo_on_merged_children(svn_depth_t depth,
2544 svn_wc_adm_access_t *adm_access,
2545 notification_receiver_baton_t *notify_b,
2546 merge_cmd_baton_t *merge_b,
2547 apr_pool_t *pool)
2549 if ((depth != svn_depth_infinity) && notify_b->merged_paths)
2551 svn_boolean_t indirect_child_mergeinfo = FALSE;
2552 apr_hash_index_t *hi;
2553 apr_hash_t *child_target_mergeinfo;
2554 const void *merged_path;
2556 for (hi = apr_hash_first(NULL, notify_b->merged_paths); hi;
2557 hi = apr_hash_next(hi))
2559 const svn_wc_entry_t *child_entry;
2560 apr_hash_this(hi, &merged_path, NULL, NULL);
2561 SVN_ERR(svn_wc__entry_versioned(&child_entry, merged_path,
2562 adm_access, FALSE, pool));
2563 if (((child_entry->kind == svn_node_dir)
2564 && (strcmp(merge_b->target, merged_path) == 0)
2565 && (depth == svn_depth_immediates))
2566 || ((child_entry->kind == svn_node_file)
2567 && (depth == svn_depth_files)))
2569 /* Set the explicit inheritable mergeinfo for,
2570 1. Merge target directory if depth is 'immediates'.
2571 2. If merge is on a file and requested depth is 'files'.
2573 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo
2574 (&child_target_mergeinfo, child_entry,
2575 &indirect_child_mergeinfo,
2576 FALSE, svn_mergeinfo_inherited,
2577 merge_b->ra_session1, merged_path,
2578 adm_access, merge_b->ctx, pool));
2579 if (indirect_child_mergeinfo)
2580 SVN_ERR(svn_client__record_wc_mergeinfo(merged_path,
2581 child_target_mergeinfo,
2582 adm_access, pool));
2586 return SVN_NO_ERROR;
2590 /* Get REVISION of the file at URL. SOURCE is a path that refers to that
2591 file's entry in the working copy, or NULL if we don't have one. Return in
2592 *FILENAME the name of a file containing the file contents, in *PROPS a hash
2593 containing the properties and in *REV the revision. All allocation occurs
2594 in POOL. */
2595 static svn_error_t *
2596 single_file_merge_get_file(const char **filename,
2597 svn_ra_session_t *ra_session,
2598 apr_hash_t **props,
2599 svn_revnum_t rev,
2600 const char *wc_target,
2601 apr_pool_t *pool)
2603 apr_file_t *fp;
2604 svn_stream_t *stream;
2606 /* ### Create this temporary file under .svn/tmp/ instead of next to
2607 ### the working file.*/
2608 SVN_ERR(svn_io_open_unique_file2(&fp, filename,
2609 wc_target, ".tmp",
2610 svn_io_file_del_none, pool));
2611 stream = svn_stream_from_aprfile2(fp, FALSE, pool);
2612 SVN_ERR(svn_ra_get_file(ra_session, "", rev,
2613 stream, NULL, props, pool));
2614 SVN_ERR(svn_stream_close(stream));
2616 return SVN_NO_ERROR;
2620 /* Send a notification specific to a single-file merge. */
2621 static APR_INLINE void
2622 single_file_merge_notify(void *notify_baton, const char *target_wcpath,
2623 svn_wc_notify_action_t action,
2624 svn_wc_notify_state_t text_state,
2625 svn_wc_notify_state_t prop_state, apr_pool_t *pool)
2627 svn_wc_notify_t *notify = svn_wc_create_notify(target_wcpath, action, pool);
2628 notify->kind = svn_node_file;
2629 notify->content_state = text_state;
2630 notify->prop_state = prop_state;
2631 if (notify->content_state == svn_wc_notify_state_missing)
2632 notify->action = svn_wc_notify_skip;
2633 notification_receiver(notify_baton, notify, pool);
2637 /* A baton for get_mergeinfo_walk_cb. */
2638 struct get_mergeinfo_walk_baton
2640 /* Access for the tree being walked. */
2641 svn_wc_adm_access_t *base_access;
2642 /* Array of paths that have explicit mergeinfo and/or are switched. */
2643 apr_array_header_t *children_with_mergeinfo;
2644 /* Merge source canonical path. */
2645 const char* merge_src_canon_path;
2647 /* Information on the merge cascaded from do_directory_merge() */
2648 const char* merge_target_path;
2649 const char *source_root_url;
2650 const char* url1;
2651 const char* url2;
2652 svn_revnum_t revision1;
2653 svn_revnum_t revision2;
2655 /* merge depth requested. */
2656 svn_depth_t depth;
2658 /* RA session and client context cascaded from do_directory_merge() */
2659 svn_ra_session_t *ra_session;
2660 svn_client_ctx_t *ctx;
2664 /* svn_wc_entry_callbacks2_t found_entry() callback for get_mergeinfo_paths.
2666 Given PATH, its corresponding ENTRY, and WB, where WB is the WALK_BATON
2667 of type "struct get_mergeinfo_walk_baton *": If PATH is switched,
2668 has explicit working svn:mergeinfo from a corresponding merge source, is
2669 missing a child due to a sparse checkout, is absent from disk, or is
2670 scheduled for deletion, then create a svn_client__merge_path_t *
2671 representing *PATH, allocated in WB->CHILDREN_WITH_MERGEINFO->POOL, and
2672 push it onto the WB->CHILDREN_WITH_MERGEINFO array. */
2673 static svn_error_t *
2674 get_mergeinfo_walk_cb(const char *path,
2675 const svn_wc_entry_t *entry,
2676 void *walk_baton,
2677 apr_pool_t *pool)
2679 struct get_mergeinfo_walk_baton *wb = walk_baton;
2680 const svn_string_t *propval;
2681 apr_hash_t *mergehash;
2682 svn_boolean_t switched = FALSE;
2683 svn_boolean_t has_mergeinfo_from_merge_src = FALSE;
2684 svn_boolean_t path_is_merge_target =
2685 !svn_path_compare_paths(path, wb->merge_target_path);
2686 const char *parent_path = svn_path_dirname(path, pool);
2688 /* We're going to receive dirents twice; we want to ignore the
2689 first one (where it's a child of a parent dir), and only use
2690 the second one (where we're looking at THIS_DIR). The exception
2691 is absent dirs, these only come through once, so continue. */
2692 if ((entry->kind == svn_node_dir)
2693 && (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) != 0)
2694 && !entry->absent)
2695 return SVN_NO_ERROR;
2697 /* Ignore the entry if it does not exist at the time of interest. */
2698 if (entry->deleted)
2699 return SVN_NO_ERROR;
2701 if (entry->absent || entry->schedule == svn_wc_schedule_delete)
2703 propval = NULL;
2704 switched = FALSE;
2706 else
2708 SVN_ERR(svn_wc_prop_get(&propval, SVN_PROP_MERGEINFO, path,
2709 wb->base_access, pool));
2710 /* We always include the merge target regardless of its mergeinfo.
2711 So we don't need to check that PATH's mergeinfo corresponds to
2712 the merge source. */
2713 if (propval && !path_is_merge_target)
2715 svn_stringbuf_t *merge_src_child_path =
2716 svn_stringbuf_create(wb->merge_src_canon_path, pool);
2718 /* When the merge target is '' or '.' WB->MERGE_TARGET_PATH is
2719 an empty string and PATH will always be relative. In this case
2720 we can safely combine WB->MERGE_SRC_CANON_PATH and PATH with
2721 svn_path_add_compent() which will supply the missing '/' separator.
2723 Otherwise WB->MERGE_TARGET_PATH is relative or absolute and
2724 we remove the common root component between WB->MERGE_TARGET_PATH
2725 and PATH from PATH before combining it with
2726 WB->MERGE_SRC_CANON_PATH. The +1 is required because if we are
2727 here that means WB->MERGE_TARGET_PATH is a proper ancestor of
2728 PATH and we must skip the path separator -- svn_path_add_compent()
2729 will add missing separators, but won't remove existing ones -- to
2730 avoid a merge_src_child_path with "//" in it. */
2731 if (strlen(wb->merge_target_path))
2732 svn_path_add_component(merge_src_child_path,
2733 path + strlen(wb->merge_target_path) + 1);
2734 else
2735 svn_path_add_component(merge_src_child_path,
2736 path);
2737 SVN_ERR(svn_mergeinfo_parse(&mergehash, propval->data, pool));
2738 if (apr_hash_get(mergehash, merge_src_child_path->data,
2739 APR_HASH_KEY_STRING))
2741 /* The easy way: PATH already has mergeinfo
2742 from this source... */
2743 has_mergeinfo_from_merge_src = TRUE;
2745 else
2747 /* ...the slightly harder way: See if PATH exists in the
2748 merge source at the revisions being merged. If it doesn't
2749 exist there is no way this subtree can be affected by the
2750 merge so we can safely leave it, and its mergeinfo, alone. */
2751 svn_error_t *err;
2752 const char *original_ra_url;
2753 const char *mergeinfo_url =
2754 svn_path_join(wb->source_root_url,
2755 /* Skip leading '/' or join won't work. */
2756 ++(merge_src_child_path->data),
2757 pool);
2758 svn_opt_revision_t *start_revision, *end_revision;
2759 const char *start_url, *end_url;
2760 svn_opt_revision_t peg_rev, rev1_opt, rev2_opt;
2762 peg_rev.value.number = wb->revision1 < wb->revision2
2763 ? wb->revision2 : wb->revision1;
2764 peg_rev.kind = svn_opt_revision_number;
2766 rev1_opt.kind = svn_opt_revision_number;
2767 rev1_opt.value.number = wb->revision1;
2769 rev2_opt.kind = svn_opt_revision_number;
2770 rev2_opt.value.number = wb->revision2;
2772 /* Instead of passing NULL to svn_client__repos_locations() and
2773 causing another session to open, reparent WB->RA_SESSION
2774 and use that. */
2775 SVN_ERR(svn_ra_get_session_url(wb->ra_session, &original_ra_url,
2776 pool));
2777 SVN_ERR(svn_ra_reparent(wb->ra_session, mergeinfo_url, pool));
2779 /* Does PATH exist in the merge source? */
2780 err = svn_client__repos_locations(&start_url, &start_revision,
2781 &end_url, &end_revision,
2782 wb->ra_session, mergeinfo_url,
2783 &peg_rev, &rev1_opt, &rev2_opt,
2784 wb->ctx, pool);
2785 if (err)
2787 /* We might see any of these errors depending on the RA
2788 access method, but they all mean that PATH doesn't exist
2789 in the merge source.
2791 ### TODO: Make svn_client__repos_locations() more
2792 ### consistent in the error it returns(?)
2794 if (err->apr_err == SVN_ERR_FS_NOT_FOUND
2795 || err->apr_err == SVN_ERR_RA_DAV_PATH_NOT_FOUND
2796 || err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
2797 svn_error_clear(err);
2798 else
2799 return err;
2802 /* Reparent the WB->RA_SESSION to its original URL. */
2803 SVN_ERR(svn_ra_reparent(wb->ra_session, original_ra_url, pool));
2805 if (!err) /* PATH does exist in the merge source*/
2807 if (propval->len > 0)
2809 has_mergeinfo_from_merge_src = TRUE;
2811 else /* Handle the special case of empty mergeinfo */
2813 /* WC->WC copies (and moves) can't contact the server
2814 and so leave empty mergefino on the copy target to
2815 prevent it from erroneously inheriting the incorrect
2816 mergeinfo from the repos (if any exists) during a
2817 merge. Reverse merges may also leave empty mergeinfo
2818 on a path so this state is not exclusive to WC->WC
2819 copies.
2821 Because the WC->WC copy behavior is a safety net,
2822 the empty mergeinfo on a path *may* not actually
2823 override anything. In the case where this is true,
2824 we don't need to treat PATH as a subtree with
2825 intersecting mergeinfo, we can simply merge into
2826 PATH's parent (which may be the merge target
2827 itself)...
2829 ...So, if we find empty mergeinfo on PATH see if
2830 PATH has any ancestor with mergeinfo. */
2831 apr_hash_t *overidden_mergeinfo;
2832 svn_boolean_t indirect;
2833 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(
2834 &overidden_mergeinfo, entry,
2835 &indirect, FALSE, svn_mergeinfo_nearest_ancestor,
2836 wb->ra_session, path, wb->base_access, wb->ctx,
2837 pool));
2838 if (indirect)
2840 /* Ok, we found empty mergeinfo which overrides some
2841 parent with mergeinfo. Time for one final
2842 optimization: If the mergeinfo overridden is
2843 *also* empty, then we can perform some preemptive
2844 elision right now and remove the empty mergeinfo
2845 from PATH. Even if the overriden parent is in
2846 the repository it's ok, since the merge target
2847 *always* gets mergeinfo set (though it may
2848 elide).*/
2849 svn_boolean_t equal_mergeinfo;
2850 SVN_ERR(svn_mergeinfo__equals(&equal_mergeinfo,
2851 mergehash,
2852 overidden_mergeinfo,
2853 FALSE, pool));
2854 if (equal_mergeinfo)
2855 SVN_ERR(svn_client__record_wc_mergeinfo(
2856 path, NULL, wb->base_access, pool));
2857 else
2858 /* The empty mergeinfo on PATH really means
2859 something. Merge this subtree separately. */
2860 has_mergeinfo_from_merge_src = TRUE;
2863 } /* (!err) */
2864 start_url = NULL;
2865 } /* the slightly harder way */
2867 /* Regardless of whether PATH has explicit mergeinfo or not, we must
2868 determine if PATH is switched. This is so get_mergeinfo_paths()
2869 can later tweak PATH's parent to reflect a missing child (implying it
2870 needs non-inheritable mergeinfo ranges) and PATH's siblings so they
2871 get their own complete set of mergeinfo. */
2872 SVN_ERR(svn_wc__path_switched(path, &switched, entry, pool));
2875 /* Store PATHs with explict mergeinfo, which are switched, are missing
2876 children due to a sparse checkout, are scheduled for deletion are absent
2877 from the WC, and/or are first level sub directories relative to merge
2878 target if depth is immediates. */
2879 if (path_is_merge_target
2880 || has_mergeinfo_from_merge_src
2881 || entry->schedule == svn_wc_schedule_delete
2882 || switched
2883 || entry->depth == svn_depth_empty
2884 || entry->depth == svn_depth_files
2885 || entry->absent
2886 || ((wb->depth == svn_depth_immediates) &&
2887 (entry->kind == svn_node_dir) &&
2888 (strcmp(parent_path, path) != 0) &&
2889 (strcmp(parent_path, wb->merge_target_path) == 0)))
2891 svn_client__merge_path_t *child =
2892 apr_pcalloc(wb->children_with_mergeinfo->pool, sizeof(*child));
2893 child->path = apr_pstrdup(wb->children_with_mergeinfo->pool, path);
2894 child->missing_child = (entry->depth == svn_depth_empty
2895 || entry->depth == svn_depth_files
2896 || ((wb->depth == svn_depth_immediates) &&
2897 (entry->kind == svn_node_dir) &&
2898 (strcmp(parent_path,
2899 wb->merge_target_path) == 0)))
2900 ? TRUE : FALSE;
2901 child->switched = switched;
2902 child->absent = entry->absent;
2903 child->scheduled_for_deletion =
2904 entry->schedule == svn_wc_schedule_delete ? TRUE : FALSE;
2905 if (propval)
2907 if (strstr(propval->data, SVN_MERGEINFO_NONINHERITABLE_STR))
2908 child->has_noninheritable = TRUE;
2909 child->propval =
2910 svn_string_create(propval->data,
2911 wb->children_with_mergeinfo->pool);
2914 /* A little trickery: If PATH doesn't have any mergeinfo or has
2915 only inheritable mergeinfo, we still describe it as having
2916 non-inheritable mergeinfo if it is missing a child. Why? Because
2917 the mergeinfo we'll add to PATH as a result of the merge will need
2918 to be non-inheritable (since PATH is missing children) and doing
2919 this now allows get_mergeinfo_paths() to properly account for PATH's
2920 other children. */
2921 if (!child->has_noninheritable
2922 && (entry->depth == svn_depth_empty
2923 || entry->depth == svn_depth_files))
2924 child->has_noninheritable = TRUE;
2926 APR_ARRAY_PUSH(wb->children_with_mergeinfo,
2927 svn_client__merge_path_t *) = child;
2930 return SVN_NO_ERROR;
2933 /* svn_wc_entry_callbacks2_t handle_error() callback for
2934 get_mergeinfo_paths().
2936 Squelch ERR by returning SVN_NO_ERROR if ERR is caused by a missing
2937 path (i.e. SVN_ERR_WC_PATH_NOT_FOUND) or an unversioned path
2938 (i.e. SVN_ERR_WC_NOT_LOCKED). */
2939 static svn_error_t *
2940 get_mergeinfo_error_handler(const char *path,
2941 svn_error_t *err,
2942 void *walk_baton,
2943 apr_pool_t *pool)
2945 svn_error_t *root_err = svn_error_root_cause(err);
2946 if (root_err == SVN_NO_ERROR)
2947 return err;
2949 switch (root_err->apr_err)
2951 case SVN_ERR_WC_PATH_NOT_FOUND:
2952 case SVN_ERR_WC_NOT_LOCKED:
2953 svn_error_clear(err);
2954 return SVN_NO_ERROR;
2956 default:
2957 return err;
2961 /* Helper for get_mergeinfo_paths()
2963 CHILDREN_WITH_MERGEINFO is a depth first sorted array filled with
2964 svn_client__merge_path_t *. Starting at the element in
2965 CHILDREN_WITH_MERGEINFO located at START_INDEX look for that
2966 element's child/parent (as indicated by LOOKING_FOR_CHILD) named
2967 PATH. If the child/parent is found, set *CHILD_OR_PARENT to that
2968 element and return the index at which if was found. If the
2969 child/parent is not found set *CHILD_OR_PARENT to NULL and return
2970 the index at which it should be inserted. */
2971 static int
2972 find_child_or_parent(apr_array_header_t *children_with_mergeinfo,
2973 svn_client__merge_path_t **child_or_parent,
2974 const char *path,
2975 svn_boolean_t looking_for_child,
2976 int start_index,
2977 apr_pool_t *pool)
2979 int j = 0;
2980 *child_or_parent = NULL;
2982 /* If possible, search forwards in the depth first sorted array
2983 to find a child PATH or backwards to find a parent PATH. */
2984 if (start_index >= 0 && start_index < children_with_mergeinfo->nelts)
2986 for (j = looking_for_child ? start_index + 1 : start_index;
2987 looking_for_child ? j < children_with_mergeinfo->nelts : j >= 0;
2988 j = looking_for_child ? j + 1 : j - 1)
2990 /* If this potential child is neither the child we are looking for
2991 or another one of PARENT's children then CHILD_PATH doesn't
2992 exist in CHILDREN_WITH_MERGEINFO. */
2993 svn_client__merge_path_t *potential_child_or_parent =
2994 APR_ARRAY_IDX(children_with_mergeinfo, j,
2995 svn_client__merge_path_t *);
2996 int cmp = svn_path_compare_paths(path,
2997 potential_child_or_parent->path);
2998 if (cmp == 0)
3000 /* Found child or parent. */
3001 *child_or_parent = potential_child_or_parent;
3002 break;
3004 else if ((looking_for_child && cmp < 0)
3005 || (!looking_for_child && cmp > 0))
3007 /* PATH doesn't exist, but found where it should be inserted. */
3008 if (!looking_for_child)
3009 j++;
3010 break;
3012 else if (!looking_for_child && j == 0)
3014 /* Looking for a parent but are at start of the array so we know
3015 where to insert the parent. */
3016 break;
3018 /* else we are looking for a child but found one of its
3019 siblings...keep looking. */
3022 return j;
3025 /* Helper for get_mergeinfo_paths()
3027 CHILDREN_WITH_MERGEINFO is a depth first sorted array filled with
3028 svn_client__merge_path_t *. Insert INSERT_ELEMENT into the
3029 CHILDREN_WITH_MERGEINFO array at index INSERT_INDEX. */
3030 static void
3031 insert_child_to_merge(apr_array_header_t *children_with_mergeinfo,
3032 svn_client__merge_path_t *insert_element,
3033 int insert_index)
3035 if (insert_index == children_with_mergeinfo->nelts)
3037 APR_ARRAY_PUSH(children_with_mergeinfo,
3038 svn_client__merge_path_t *) = insert_element;
3040 else
3042 /* Copy the last element of CHILDREN_WITH_MERGEINFO and add it to the
3043 end of the array. */
3044 int j;
3045 svn_client__merge_path_t *curr =
3046 APR_ARRAY_IDX(children_with_mergeinfo,
3047 children_with_mergeinfo->nelts - 1,
3048 svn_client__merge_path_t *);
3049 svn_client__merge_path_t *curr_copy =
3050 apr_palloc(children_with_mergeinfo->pool, sizeof(*curr_copy));
3052 *curr_copy = *curr;
3053 APR_ARRAY_PUSH(children_with_mergeinfo,
3054 svn_client__merge_path_t *) = curr_copy;
3056 /* Move all elements from INSERT_INDEX to the end of the array forward
3057 one spot then insert the new element. */
3058 for (j = children_with_mergeinfo->nelts - 2; j >= insert_index; j--)
3060 svn_client__merge_path_t *prev;
3061 curr = APR_ARRAY_IDX(children_with_mergeinfo, j,
3062 svn_client__merge_path_t *);
3063 if (j == insert_index)
3064 *curr = *insert_element;
3065 else
3067 prev = APR_ARRAY_IDX(children_with_mergeinfo, j - 1,
3068 svn_client__merge_path_t *);
3069 *curr = *prev;
3075 /* Helper for get_mergeinfo_paths()'s qsort() call. */
3076 static int
3077 compare_merge_path_t_as_paths(const void *a,
3078 const void *b)
3080 svn_client__merge_path_t *child1 = *((svn_client__merge_path_t * const *) a);
3081 svn_client__merge_path_t *child2 = *((svn_client__merge_path_t * const *) b);
3083 return svn_path_compare_paths(child1->path, child2->path);
3086 /* Helper for get_mergeinfo_paths(). If CHILD->PATH is switched,
3087 absent, or scheduled for deletion make sure its parent is marked
3088 as missing a child. Start looking up for parent from *CURR_INDEX
3089 in CHILDREN_WITH_MERGEINFO. Create the parent and insert it into
3090 CHILDREN_WITH_MERGEINFO if necessary (and increment *CURR_INDEX
3091 so that caller don't process the inserted element). Also ensure
3092 that CHILD->PATH's siblings which are not already present in
3093 CHILDREN_WITH_MERGEINFO are also added to the array. Use POOL for
3094 all temporary allocations. */
3095 static svn_error_t *
3096 insert_parent_and_sibs_of_sw_absent_del_entry(
3097 apr_array_header_t *children_with_mergeinfo,
3098 merge_cmd_baton_t *merge_cmd_baton,
3099 int *curr_index,
3100 svn_client__merge_path_t *child,
3101 svn_wc_adm_access_t *adm_access,
3102 apr_pool_t *pool)
3104 svn_client__merge_path_t *parent;
3105 const char *parent_path = svn_path_dirname(child->path, pool);
3106 apr_hash_t *entries;
3107 apr_hash_index_t *hi;
3108 svn_wc_adm_access_t *parent_access;
3109 int insert_index, parent_index;
3111 if (!(child->absent
3112 || child->scheduled_for_deletion
3113 || (child->switched
3114 && strcmp(merge_cmd_baton->target, child->path) != 0)))
3115 return SVN_NO_ERROR;
3117 parent_index = find_child_or_parent(children_with_mergeinfo, &parent,
3118 parent_path, FALSE, *curr_index, pool);
3119 if (parent)
3121 parent->missing_child = TRUE;
3123 else
3125 /* Create a new element to insert into CHILDREN_WITH_MERGEINFO. */
3126 parent = apr_pcalloc(children_with_mergeinfo->pool, sizeof(*parent));
3127 parent->path = apr_pstrdup(children_with_mergeinfo->pool, parent_path);
3128 parent->missing_child = TRUE;
3129 /* Insert PARENT into CHILDREN_WITH_MERGEINFO. */
3130 insert_child_to_merge(children_with_mergeinfo, parent, parent_index);
3131 /* Increment for loop index so we don't process the inserted element. */
3132 (*curr_index)++;
3133 } /*(parent == NULL) */
3135 /* Add all of PARENT's non-missing children that are not already present.*/
3136 SVN_ERR(svn_wc_adm_probe_try3(&parent_access, adm_access, parent->path,
3137 TRUE, -1, merge_cmd_baton->ctx->cancel_func,
3138 merge_cmd_baton->ctx->cancel_baton, pool));
3139 SVN_ERR(svn_wc_entries_read(&entries, parent_access, FALSE, pool));
3140 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
3142 const void *key;
3143 svn_client__merge_path_t *sibling_of_missing;
3144 const char *child_path;
3146 apr_hash_this(hi, &key, NULL, NULL);
3148 if (strcmp(key, SVN_WC_ENTRY_THIS_DIR) == 0)
3149 continue;
3151 /* Does this child already exist in CHILDREN_WITH_MERGEINFO? */
3152 child_path = svn_path_join(parent->path, key, pool);
3153 insert_index = find_child_or_parent(children_with_mergeinfo,
3154 &sibling_of_missing, child_path,
3155 TRUE, parent_index, pool);
3156 /* Create the missing child and insert it into CHILDREN_WITH_MERGEINFO.*/
3157 if (!sibling_of_missing)
3159 sibling_of_missing = apr_pcalloc(children_with_mergeinfo->pool,
3160 sizeof(*sibling_of_missing));
3161 sibling_of_missing->path = apr_pstrdup(children_with_mergeinfo->pool,
3162 child_path);
3163 insert_child_to_merge(children_with_mergeinfo, sibling_of_missing,
3164 insert_index);
3167 return SVN_NO_ERROR;
3170 /* Helper for do_directory_merge()
3172 Perform a depth first walk of the working copy tree rooted at
3173 MERGE_CMD_BATON->TARGET (with the corresponding ENTRY). Create an
3174 svn_client__merge_path_t * for any path which meets one or more of the
3175 following criteria:
3177 1) Path has working svn:mergeinfo from corresponding merge source.
3178 2) Path is switched.
3179 3) Path has no mergeinfo of it's own but it's parent has mergeinfo with
3180 non-inheritable ranges (in this case the function will actually set
3181 override mergeinfo on the path if this isn't a dry-run and the merge
3182 is between differences in the same repository).
3183 4) Path has an immediate child (or children) missing from the WC because
3184 the child is switched or absent from the WC, or due to a sparse
3185 checkout.
3186 5) Path has a sibling (or siblings) missing from the WC because the
3187 sibling is switched, absent, schduled for deletion, or missing due to
3188 a sparse checkout.
3189 6) Path is absent from disk due to an authz restriction.
3190 7) Path is scheduled for deletion.
3191 8) Path is equal to MERGE_CMD_BATON->TARGET.
3193 Store the svn_client__merge_path_t *'s in *CHILDREN_WITH_MERGEINFO in
3194 depth-first order based on the svn_client__merge_path_t *s path member as
3195 sorted by svn_path_compare_paths().
3197 Note: Since the walk is rooted at MERGE_CMD_BATON->TARGET, the latter is
3198 guaranteed to be in *CHILDREN_WITH_MERGEINFO and due to the depth-first
3199 ordering it is guaranteed to be the first element in
3200 *CHILDREN_WITH_MERGEINFO.
3202 Cascade MERGE_SRC_CANON_PATH. */
3203 static svn_error_t *
3204 get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
3205 merge_cmd_baton_t *merge_cmd_baton,
3206 const char* merge_src_canon_path,
3207 const svn_wc_entry_t *entry,
3208 const char *source_root_url,
3209 const char *url1,
3210 const char *url2,
3211 svn_revnum_t revision1,
3212 svn_revnum_t revision2,
3213 svn_ra_session_t *ra_session,
3214 svn_wc_adm_access_t *adm_access,
3215 svn_client_ctx_t *ctx,
3216 svn_depth_t depth,
3217 apr_pool_t *pool)
3219 int i;
3220 apr_pool_t *iterpool;
3221 static const svn_wc_entry_callbacks2_t walk_callbacks =
3222 { get_mergeinfo_walk_cb, get_mergeinfo_error_handler };
3223 struct get_mergeinfo_walk_baton wb =
3224 { adm_access, children_with_mergeinfo,
3225 merge_src_canon_path, merge_cmd_baton->target, source_root_url,
3226 url1, url2, revision1, revision2, depth, ra_session, ctx };
3228 /* Cover cases 1), 2), 6), and 8) by walking the WC to get all paths which
3229 have mergeinfo and/or are switched or are absent from disk or is the
3230 target of the merge. */
3231 if (entry->kind == svn_node_file)
3232 SVN_ERR(walk_callbacks.found_entry(merge_cmd_baton->target, entry, &wb,
3233 pool));
3234 else
3235 SVN_ERR(svn_wc_walk_entries3(merge_cmd_baton->target, adm_access,
3236 &walk_callbacks, &wb, depth, TRUE,
3237 merge_cmd_baton->ctx->cancel_func,
3238 merge_cmd_baton->ctx->cancel_baton,
3239 pool));
3241 /* CHILDREN_WITH_MERGEINFO must be in depth first order, but
3242 svn_wc_walk_entries3() relies on svn_wc_entries_read() which means the
3243 paths at a given directory level are not in any particular order. Also,
3244 we may need to add elements to the array to cover case 3) through 5) from
3245 the docstring. If so, it is more efficient to find and insert these
3246 paths if the sibling paths are in a guaranteed depth-first order. For
3247 the first reason we sort the array, for the second reason we do it now
3248 rather than at the end of this function. */
3249 qsort(children_with_mergeinfo->elts,
3250 children_with_mergeinfo->nelts,
3251 children_with_mergeinfo->elt_size,
3252 compare_merge_path_t_as_paths);
3254 iterpool = svn_pool_create(pool);
3255 for (i = 0; i < children_with_mergeinfo->nelts; i++)
3257 int insert_index;
3258 svn_client__merge_path_t *child =
3259 APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
3260 svn_pool_clear(iterpool);
3262 /* Case 3) Where merging to a path with a switched child the path gets
3263 non-inheritable mergeinfo for the merge range performed and the child
3264 gets it's own set of mergeinfo. If the switched child later
3265 "returns", e.g. a switched path is unswitched, the child may not have
3266 any explicit mergeinfo. If the initial merge is repeated we don't
3267 want to repeat the merge for the path, but we do want to repeat it
3268 for the previously switched child. To ensure this we check if all
3269 of CHILD's non-missing children have explicit mergeinfo (they should
3270 already be present in CHILDREN_WITH_MERGEINFO if they do). If not,
3271 add the children without mergeinfo to CHILDREN_WITH_MERGEINFO so
3272 do_directory_merge() will merge them independently.
3274 But that's not enough! Since do_directory_merge() performs
3275 the merges on the paths in CHILDREN_WITH_MERGEINFO in a depth first
3276 manner it will merge the previously switched path's parent first. As
3277 part of this merge it will update the parent's previously
3278 non-inheritable mergeinfo and make it inheritable (since it notices
3279 the path has no missing children), then when
3280 do_directory_merge() finally merges the previously missing
3281 child it needs to get mergeinfo from the child's nearest ancestor,
3282 but since do_directory_merge() already tweaked that
3283 mergeinfo, removing the non-inheritable flag, it appears that the
3284 child already has been merged to. To prevent this we set override
3285 mergeinfo on the child now, before any merging is done, so it has
3286 explicit mergeinfo that reflects only CHILD's inheritable
3287 mergeinfo. */
3289 if (child->has_noninheritable)
3291 apr_hash_t *entries;
3292 apr_hash_index_t *hi;
3293 svn_wc_adm_access_t *child_access;
3294 SVN_ERR(svn_wc_adm_probe_try3(&child_access, adm_access,
3295 child->path, TRUE, -1,
3296 merge_cmd_baton->ctx->cancel_func,
3297 merge_cmd_baton->ctx->cancel_baton,
3298 iterpool));
3299 SVN_ERR(svn_wc_entries_read(&entries, child_access, FALSE,
3300 iterpool));
3301 for (hi = apr_hash_first(iterpool, entries); hi;
3302 hi = apr_hash_next(hi))
3304 const void *key;
3305 svn_client__merge_path_t *child_of_noninheritable;
3306 const char *child_path;
3308 apr_hash_this(hi, &key, NULL, NULL);
3310 if (strcmp(key, SVN_WC_ENTRY_THIS_DIR) == 0)
3311 continue;
3313 /* Does this child already exist in CHILDREN_WITH_MERGEINFO? If
3314 not, create it and insert it into CHILDREN_WITH_MERGEINFO and
3315 set override mergeinfo on it. */
3316 child_path = svn_path_join(child->path, key, iterpool);
3317 insert_index = find_child_or_parent(children_with_mergeinfo,
3318 &child_of_noninheritable,
3319 child_path, TRUE, i,
3320 iterpool);
3321 if (!child_of_noninheritable)
3323 child_of_noninheritable =
3324 apr_pcalloc(children_with_mergeinfo->pool,
3325 sizeof(*child_of_noninheritable));
3326 child_of_noninheritable->path =
3327 apr_pstrdup(children_with_mergeinfo->pool, child_path);
3328 insert_child_to_merge(children_with_mergeinfo,
3329 child_of_noninheritable,
3330 insert_index);
3331 if (!merge_cmd_baton->dry_run
3332 && merge_cmd_baton->same_repos)
3334 svn_boolean_t inherited;
3335 apr_hash_t *mergeinfo;
3336 merge_cmd_baton->override_set = TRUE;
3337 SVN_ERR(svn_client__get_wc_mergeinfo
3338 (&mergeinfo, &inherited, FALSE,
3339 svn_mergeinfo_nearest_ancestor,
3340 entry, child_of_noninheritable->path,
3341 merge_cmd_baton->target, NULL, adm_access,
3342 merge_cmd_baton->ctx, iterpool));
3343 SVN_ERR(svn_client__record_wc_mergeinfo(
3344 child_of_noninheritable->path, mergeinfo, adm_access,
3345 iterpool));
3350 /* Case 4, 5, and 7 are handled by the following function. */
3351 SVN_ERR(insert_parent_and_sibs_of_sw_absent_del_entry(
3352 children_with_mergeinfo, merge_cmd_baton, &i, child,
3353 adm_access, iterpool));
3354 } /* i < children_with_mergeinfo->nelts */
3356 svn_pool_destroy(iterpool);
3357 return SVN_NO_ERROR;
3361 /*-----------------------------------------------------------------------*/
3363 /*** Merge Source Normalization ***/
3365 typedef struct merge_source_t
3367 /* "left" side URL and revision (inclusive iff youngest) */
3368 const char *url1;
3369 svn_revnum_t rev1;
3371 /* "right" side URL and revision (inclusive iff youngest) */
3372 const char *url2;
3373 svn_revnum_t rev2;
3375 } merge_source_t;
3377 /* qsort-compatible sort routine, rating merge_source_t * objects to
3378 be in descending (youngest-to-oldest) order based on their ->rev1
3379 component. */
3380 static int
3381 compare_merge_source_ts(const void *a,
3382 const void *b)
3384 svn_revnum_t a_rev = ((const merge_source_t *)a)->rev1;
3385 svn_revnum_t b_rev = ((const merge_source_t *)b)->rev1;
3386 if (a_rev == b_rev)
3387 return 0;
3388 return a_rev < b_rev ? 1 : -1;
3391 /* Set MERGE_SOURCE_TS_P to a list of merge sources generated by
3392 slicing history location SEGMENTS with a given requested merge
3393 RANGE. Use SOURCE_ROOT_URL for full source URL calculation. */
3394 static svn_error_t *
3395 combine_range_with_segments(apr_array_header_t **merge_source_ts_p,
3396 svn_merge_range_t *range,
3397 apr_array_header_t *segments,
3398 const char *source_root_url,
3399 apr_pool_t *pool)
3401 apr_array_header_t *merge_source_ts =
3402 apr_array_make(pool, 1, sizeof(merge_source_t *));
3403 svn_revnum_t minrev = MIN(range->start, range->end) + 1;
3404 svn_revnum_t maxrev = MAX(range->start, range->end);
3405 svn_boolean_t subtractive = (range->start > range->end);
3406 int i;
3408 for (i = 0; i < segments->nelts; i++)
3410 svn_location_segment_t *segment =
3411 APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
3412 merge_source_t *merge_source;
3413 const char *path1 = NULL;
3414 svn_revnum_t rev1;
3416 /* If this segment doesn't overlap our range at all, or
3417 represents a gap, ignore it. */
3418 if ((segment->range_end < minrev)
3419 || (segment->range_start > maxrev)
3420 || (! segment->path))
3421 continue;
3423 /* If our range spans a segment boundary, we have to point our
3424 merge_source_t's path1 to the path of the immediately older
3425 segment, else it points to the same location as its path2. */
3426 rev1 = MAX(segment->range_start, minrev) - 1;
3427 if (minrev <= segment->range_start)
3429 if (i > 0)
3431 path1 = (APR_ARRAY_IDX(segments, i - 1,
3432 svn_location_segment_t *))->path;
3434 /* If we've backed PATH1 up into a segment gap, let's back
3435 it up further still to the segment before the gap. We'll
3436 have to adjust rev1, too. */
3437 if ((! path1) && (i > 1))
3439 path1 = (APR_ARRAY_IDX(segments, i - 2,
3440 svn_location_segment_t *))->path;
3441 rev1 = (APR_ARRAY_IDX(segments, i - 2,
3442 svn_location_segment_t *))->range_end;
3445 else
3447 path1 = apr_pstrdup(pool, segment->path);
3450 /* If we don't have two valid paths, we won't know what to do
3451 when merging. This could happen if someone requested a merge
3452 where the source didn't exist in a particular revision or
3453 something. The merge code would probably bomb out anyway, so
3454 we'll just *not* create a merge source in this case. */
3455 if (! (path1 && segment->path))
3456 continue;
3458 /* Build our merge source structure. */
3459 merge_source = apr_pcalloc(pool, sizeof(*merge_source));
3460 merge_source->url1 = svn_path_join(source_root_url,
3461 svn_path_uri_encode(path1,
3462 pool), pool);
3463 merge_source->url2 = svn_path_join(source_root_url,
3464 svn_path_uri_encode(segment->path,
3465 pool), pool);
3466 merge_source->rev1 = rev1;
3467 merge_source->rev2 = MIN(segment->range_end, maxrev);
3469 /* If this is subtractive, reverse the whole calculation. */
3470 if (subtractive)
3472 svn_revnum_t tmprev = merge_source->rev1;
3473 const char *tmpurl = merge_source->url1;
3474 merge_source->rev1 = merge_source->rev2;
3475 merge_source->url1 = merge_source->url2;
3476 merge_source->rev2 = tmprev;
3477 merge_source->url2 = tmpurl;
3480 APR_ARRAY_PUSH(merge_source_ts, merge_source_t *) = merge_source;
3483 /* If this was a subtractive merge, and we created more than one
3484 merge source, we need to reverse the sort ordering of our sources. */
3485 if (subtractive && (merge_source_ts->nelts > 1))
3486 qsort(merge_source_ts->elts, merge_source_ts->nelts,
3487 merge_source_ts->elt_size, compare_merge_source_ts);
3489 *merge_source_ts_p = merge_source_ts;
3490 return SVN_NO_ERROR;
3493 /* Set *MERGE_SOURCES to an array of merge_source_t * objects, each
3494 holding the paths and revisions needed to fully describe a range of
3495 requested merges. Determine the requested merges by examining
3496 SOURCE (and its associated URL, SOURCE_URL) and PEG_REVISION (which
3497 specifies the line of history from which merges will be pulled) and
3498 RANGES_TO_MERGE (a list of svn_opt_revision_range_t's which provide
3499 revision ranges).
3501 If PEG_REVISION is unspecified, treat that it as HEAD.
3503 SOURCE_ROOT_URL is the root URL of the source repository.
3505 Use RA_SESSION -- whose session URL matches SOURCE_URL -- to answer
3506 historical questions.
3508 CTX is a client context baton.
3510 Use POOL for all allocation.
3512 See `MERGEINFO MERGE SOURCE NORMALIZATION' for more on the
3513 background of this function.
3515 static svn_error_t *
3516 normalize_merge_sources(apr_array_header_t **merge_sources_p,
3517 const char *source,
3518 const char *source_url,
3519 const char *source_root_url,
3520 const svn_opt_revision_t *peg_revision,
3521 const apr_array_header_t *ranges_to_merge,
3522 svn_ra_session_t *ra_session,
3523 svn_client_ctx_t *ctx,
3524 apr_pool_t *pool)
3526 svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
3527 svn_revnum_t peg_revnum;
3528 svn_revnum_t oldest_requested = SVN_INVALID_REVNUM;
3529 svn_revnum_t youngest_requested = SVN_INVALID_REVNUM;
3530 svn_opt_revision_t youngest_opt_rev;
3531 apr_array_header_t *merge_range_ts, *segments;
3532 apr_pool_t *subpool;
3533 int i;
3534 youngest_opt_rev.kind = svn_opt_revision_head;
3536 /* Initialize our return variable. */
3537 *merge_sources_p = apr_array_make(pool, 1, sizeof(merge_source_t *));
3539 /* Resolve our PEG_REVISION to a real number. */
3540 SVN_ERR(svn_client__get_revision_number(&peg_revnum, &youngest_rev,
3541 ra_session, peg_revision,
3542 source, pool));
3543 if (! SVN_IS_VALID_REVNUM(peg_revnum))
3544 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
3546 /* Create a list to hold svn_merge_range_t's. */
3547 merge_range_ts = apr_array_make(pool, ranges_to_merge->nelts,
3548 sizeof(svn_merge_range_t *));
3550 subpool = svn_pool_create(pool);
3551 for (i = 0; i < ranges_to_merge->nelts; i++)
3553 svn_revnum_t range_start_rev, range_end_rev;
3554 svn_opt_revision_t *range_start =
3555 &((APR_ARRAY_IDX(ranges_to_merge, i,
3556 svn_opt_revision_range_t *))->start);
3557 svn_opt_revision_t *range_end =
3558 &((APR_ARRAY_IDX(ranges_to_merge, i,
3559 svn_opt_revision_range_t *))->end);
3561 svn_pool_clear(subpool);
3563 /* Resolve revisions to real numbers, validating as we go. */
3564 if ((range_start->kind == svn_opt_revision_unspecified)
3565 || (range_end->kind == svn_opt_revision_unspecified))
3566 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
3567 _("Not all required revisions are specified"));
3568 SVN_ERR(svn_client__get_revision_number(&range_start_rev, &youngest_rev,
3569 ra_session, range_start,
3570 source, subpool));
3571 SVN_ERR(svn_client__get_revision_number(&range_end_rev, &youngest_rev,
3572 ra_session, range_end,
3573 source, subpool));
3575 /* If this isn't a no-op range... */
3576 if (range_start_rev != range_end_rev)
3578 /* ...then create an svn_merge_range_t object for it. */
3579 svn_merge_range_t *range = apr_pcalloc(pool, sizeof(*range));
3580 range->start = range_start_rev;
3581 range->end = range_end_rev;
3582 range->inheritable = TRUE;
3584 /* Add our merge range to our list thereof. */
3585 APR_ARRAY_PUSH(merge_range_ts, svn_merge_range_t *) = range;
3589 /* Okay. We have a list of svn_merge_range_t's. Now, we need to
3590 compact that list to remove redundances and such. */
3591 SVN_ERR(compact_merge_ranges(&merge_range_ts, merge_range_ts, pool));
3593 /* No compacted ranges to merge? No problem. */
3594 if (merge_range_ts->nelts == 0)
3595 return SVN_NO_ERROR;
3597 /* Find the extremes of the revisions across our set of ranges. */
3598 for (i = 0; i < merge_range_ts->nelts; i++)
3600 svn_merge_range_t *range =
3601 APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *);
3602 svn_revnum_t minrev = MIN(range->start, range->end);
3603 svn_revnum_t maxrev = MAX(range->start, range->end);
3605 /* Keep a running tally of the oldest and youngest requested
3606 revisions. */
3607 if ((! SVN_IS_VALID_REVNUM(oldest_requested))
3608 || (minrev < oldest_requested))
3609 oldest_requested = minrev;
3610 if ((! SVN_IS_VALID_REVNUM(youngest_requested))
3611 || (maxrev > youngest_requested))
3612 youngest_requested = maxrev;
3615 /* ### FIXME: Our underlying APIs can't yet handle the case where
3616 the peg revision isn't the youngest of the three revisions. So
3617 we'll just verify that the source in the peg revision is related
3618 to the the source in the youngest requested revision (which is
3619 all the underlying APIs would do in this case right now anyway). */
3620 if (peg_revnum < youngest_requested)
3622 const char *start_url;
3623 svn_opt_revision_t requested, unspec, pegrev, *start_revision;
3624 unspec.kind = svn_opt_revision_unspecified;
3625 requested.kind = svn_opt_revision_number;
3626 requested.value.number = youngest_requested;
3627 pegrev.kind = svn_opt_revision_number;
3628 pegrev.value.number = peg_revnum;
3630 SVN_ERR(svn_client__repos_locations(&start_url, &start_revision,
3631 NULL, NULL,
3632 ra_session, source_url,
3633 &pegrev, &requested,
3634 &unspec, ctx, pool));
3635 peg_revnum = youngest_requested;
3638 /* Fetch the locations for our merge range span. */
3639 SVN_ERR(svn_client__repos_location_segments(&segments,
3640 ra_session, "",
3641 peg_revnum,
3642 youngest_requested,
3643 oldest_requested,
3644 ctx, pool));
3646 /* For each range in our requested range set, try to determine the
3647 path(s) associated with that range. */
3648 for (i = 0; i < merge_range_ts->nelts; i++)
3650 svn_merge_range_t *range =
3651 APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *);
3652 apr_array_header_t *merge_sources;
3653 int j;
3655 /* Copy the resulting merge sources into master list thereof. */
3656 SVN_ERR(combine_range_with_segments(&merge_sources, range,
3657 segments, source_root_url, pool));
3658 for (j = 0; j < merge_sources->nelts; j++)
3660 APR_ARRAY_PUSH(*merge_sources_p, merge_source_t *) =
3661 APR_ARRAY_IDX(merge_sources, j, merge_source_t *);
3665 return SVN_NO_ERROR;
3669 /*-----------------------------------------------------------------------*/
3671 /*** Merge Workhorse Functions ***/
3673 /* The single-file, simplified version of do_directory_merge(), which see for
3674 parameter descriptions.
3676 Additional parameters:
3678 If SOURCES_RELATED is set, the "left" and "right" sides of the
3679 merge source are historically related (ancestors, uncles, second
3680 cousins thrice removed, etc...). (This is used to simulate the
3681 history checks that the repository logic does in the directory case.)
3683 static svn_error_t *
3684 do_file_merge(const char *url1,
3685 svn_revnum_t revision1,
3686 const char *url2,
3687 svn_revnum_t revision2,
3688 const char *target_wcpath,
3689 svn_boolean_t sources_related,
3690 svn_wc_adm_access_t *adm_access,
3691 notification_receiver_baton_t *notify_b,
3692 merge_cmd_baton_t *merge_b,
3693 apr_pool_t *pool)
3695 svn_error_t *err = SVN_NO_ERROR;
3696 apr_hash_t *props1, *props2;
3697 const char *tmpfile1, *tmpfile2;
3698 const char *mimetype1, *mimetype2;
3699 svn_string_t *pval;
3700 apr_array_header_t *propchanges, *remaining_ranges;
3701 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
3702 svn_wc_notify_state_t text_state = svn_wc_notify_state_unknown;
3703 svn_client_ctx_t *ctx = merge_b->ctx;
3704 const char *mergeinfo_path;
3705 svn_merge_range_t range;
3706 apr_hash_t *target_mergeinfo;
3707 const svn_wc_entry_t *entry;
3708 int i;
3709 svn_boolean_t indirect = FALSE;
3710 apr_pool_t *subpool;
3711 svn_boolean_t is_rollback = (revision1 > revision2);
3712 const char *primary_url = is_rollback ? url1 : url2;
3713 svn_boolean_t honor_mergeinfo, record_mergeinfo;
3715 mergeinfo_behavior(&honor_mergeinfo, &record_mergeinfo, merge_b);
3717 /* Note that this is a single-file merge. */
3718 notify_b->is_single_file_merge = TRUE;
3720 /* Ensure that the adm_access we're playing with is our TARGET_WCPATH's
3721 parent, as required by some of underlying helper functions. */
3722 SVN_ERR(svn_wc_adm_probe_try3(&adm_access, adm_access, target_wcpath,
3723 TRUE, -1, merge_b->ctx->cancel_func,
3724 merge_b->ctx->cancel_baton,
3725 pool));
3727 SVN_ERR(svn_wc__entry_versioned(&entry, target_wcpath, adm_access, FALSE,
3728 pool));
3730 range.start = revision1;
3731 range.end = revision2;
3732 range.inheritable = TRUE;
3733 if (honor_mergeinfo)
3735 const char *source_root_url;
3736 apr_hash_t *implicit_mergeinfo;
3738 /* Fetch mergeinfo (temporarily reparenting ra_session1 to
3739 working copy target URL). */
3740 SVN_ERR(svn_ra_reparent(merge_b->ra_session1, entry->url, pool));
3741 SVN_ERR(get_full_mergeinfo(&target_mergeinfo, &implicit_mergeinfo, entry,
3742 &indirect, svn_mergeinfo_inherited,
3743 merge_b->ra_session1, target_wcpath,
3744 MAX(revision1, revision2),
3745 MIN(revision1, revision2),
3746 adm_access, ctx, pool));
3747 SVN_ERR(svn_ra_reparent(merge_b->ra_session1, url1, pool));
3749 /* Calculate remaining merges. */
3750 SVN_ERR(svn_ra_get_repos_root(merge_b->ra_session1,
3751 &source_root_url, pool));
3752 SVN_ERR(svn_client__path_relative_to_root(&mergeinfo_path, primary_url,
3753 source_root_url, TRUE, NULL,
3754 NULL, pool));
3755 SVN_ERR(calculate_remaining_ranges(&remaining_ranges, source_root_url,
3756 url1, revision1, url2, revision2,
3757 TRUE, target_mergeinfo,
3758 implicit_mergeinfo,
3759 merge_b->ra_session1,
3760 entry, ctx, pool));
3762 else
3764 remaining_ranges = apr_array_make(pool, 1, sizeof(&range));
3765 APR_ARRAY_PUSH(remaining_ranges, svn_merge_range_t *) = &range;
3768 subpool = svn_pool_create(pool);
3770 for (i = 0; i < remaining_ranges->nelts; i++)
3772 svn_wc_notify_t *n;
3774 /* When using this merge range, account for the exclusivity of
3775 its low value (which is indicated by this operation being a
3776 merge vs. revert). */
3777 svn_merge_range_t *r = APR_ARRAY_IDX(remaining_ranges, i,
3778 svn_merge_range_t *);
3780 svn_pool_clear(subpool);
3782 n = svn_wc_create_notify(target_wcpath,
3783 svn_wc_notify_merge_begin,
3784 subpool);
3785 if (merge_b->sources_ancestral)
3786 n->merge_range = r;
3787 notification_receiver(notify_b, n, subpool);
3789 /* While we currently don't allow it, in theory we could be
3790 fetching two fulltexts from two different repositories here. */
3791 SVN_ERR(single_file_merge_get_file(&tmpfile1, merge_b->ra_session1,
3792 &props1, r->start, target_wcpath,
3793 subpool));
3794 SVN_ERR(single_file_merge_get_file(&tmpfile2, merge_b->ra_session2,
3795 &props2, r->end, target_wcpath,
3796 subpool));
3798 /* Discover any svn:mime-type values in the proplists */
3799 pval = apr_hash_get(props1, SVN_PROP_MIME_TYPE,
3800 strlen(SVN_PROP_MIME_TYPE));
3801 mimetype1 = pval ? pval->data : NULL;
3803 pval = apr_hash_get(props2, SVN_PROP_MIME_TYPE,
3804 strlen(SVN_PROP_MIME_TYPE));
3805 mimetype2 = pval ? pval->data : NULL;
3807 /* Deduce property diffs. */
3808 SVN_ERR(svn_prop_diffs(&propchanges, props2, props1, subpool));
3810 /* If we aren't ignoring ancestry, then we've already done
3811 ancestry relatedness checks. If we are ignoring ancestry, or
3812 our sources are known to be related, then we can do
3813 text-n-props merge; otherwise, we have to do a delete-n-add
3814 merge. */
3815 if (! (merge_b->ignore_ancestry || sources_related))
3817 /* Delete... */
3818 SVN_ERR(merge_file_deleted(adm_access,
3819 &text_state,
3820 target_wcpath,
3821 NULL,
3822 NULL,
3823 mimetype1, mimetype2,
3824 props1,
3825 merge_b));
3826 single_file_merge_notify(notify_b, target_wcpath,
3827 svn_wc_notify_update_delete, text_state,
3828 svn_wc_notify_state_unknown, subpool);
3830 /* ...plus add... */
3831 SVN_ERR(merge_file_added(adm_access,
3832 &text_state, &prop_state,
3833 target_wcpath,
3834 tmpfile1,
3835 tmpfile2,
3836 r->start,
3837 r->end,
3838 mimetype1, mimetype2,
3839 propchanges, props1,
3840 merge_b));
3841 single_file_merge_notify(notify_b, target_wcpath,
3842 svn_wc_notify_update_add, text_state,
3843 prop_state, subpool);
3844 /* ... equals replace. */
3846 else
3848 SVN_ERR(merge_file_changed(adm_access,
3849 &text_state, &prop_state,
3850 target_wcpath,
3851 tmpfile1,
3852 tmpfile2,
3853 r->start,
3854 r->end,
3855 mimetype1, mimetype2,
3856 propchanges, props1,
3857 merge_b));
3858 single_file_merge_notify(notify_b, target_wcpath,
3859 svn_wc_notify_update_update, text_state,
3860 prop_state, subpool);
3863 /* Ignore if temporary file not found. It may have been renamed. */
3864 /* (This is where we complain about missing Lisp, or better yet,
3865 Python...) */
3866 err = svn_io_remove_file(tmpfile1, subpool);
3867 if (err && ! APR_STATUS_IS_ENOENT(err->apr_err))
3868 return err;
3869 svn_error_clear(err);
3870 err = SVN_NO_ERROR;
3871 err = svn_io_remove_file(tmpfile2, subpool);
3872 if (err && ! APR_STATUS_IS_ENOENT(err->apr_err))
3873 return err;
3874 svn_error_clear(err);
3875 err = SVN_NO_ERROR;
3877 if (i < remaining_ranges->nelts - 1 &&
3878 is_path_conflicted_by_merge(merge_b))
3880 err = make_merge_conflict_error(target_wcpath, r, pool);
3881 break;
3885 /* Record updated WC mergeinfo to account for our new merges, minus
3886 any unresolved conflicts and skips. */
3887 if (record_mergeinfo && remaining_ranges->nelts)
3889 apr_hash_t *merges;
3890 SVN_ERR(determine_merges_performed(&merges, target_wcpath,
3891 &range, svn_depth_infinity,
3892 adm_access, notify_b, merge_b,
3893 subpool));
3894 /* If this whole merge was simply a no-op merge to a file then
3895 we don't touch the local mergeinfo. */
3896 if (merge_b->operative_merge)
3898 /* If merge target has indirect mergeinfo set it before
3899 recording the first merge range. */
3900 if (indirect)
3901 SVN_ERR(svn_client__record_wc_mergeinfo(target_wcpath,
3902 target_mergeinfo,
3903 adm_access, subpool));
3905 SVN_ERR(update_wc_mergeinfo(target_wcpath, entry, mergeinfo_path,
3906 merges, is_rollback, adm_access,
3907 ctx, subpool));
3911 svn_pool_destroy(subpool);
3913 /* Sleep to ensure timestamp integrity. */
3914 svn_sleep_for_timestamps();
3916 return err;
3920 /* Perform a merge of changes between URL1@REVISION1 and
3921 URL2@REVISION2, applied to the children of PARENT_ENTRY. URL1,
3922 URL2, and PARENT_ENTRY all represent directories -- for the single
3923 file case, the caller should use do_file_merge().
3925 If MERGE_B->sources_ancestral is set, then URL1@REVISION1 must be a
3926 historical ancestor of URL2@REVISION2, or vice-versa (see
3927 `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements around
3928 the values of URL1, REVISION1, URL2, and REVISION2 in this case).
3930 Handle DEPTH as documented for svn_client_merge3().
3932 CHILDREN_WITH_MERGEINFO may contain child paths (svn_client__merge_path_t *)
3933 which are switched or which have mergeinfo which differs from that of the
3934 merge target root (ignored if empty or NULL). CHILDREN_WITH_MERGEINFO
3935 list should have entries sorted in depth first order as mandated by the
3936 reporter API. Because of this, we drive the diff editor in such a way that
3937 it avoids merging child paths when a merge is driven for their parent path.
3939 CHILDREN_WITH_MERGEINFO may contain TARGET_WCPATH (which may be
3940 MERGE_B->TARGET), in that case TARGET_INDEX is the array index for
3941 TARGET_WCPATH, otherwise it should be set to a negative value.
3943 NOTE: This is a wrapper around drive_merge_report_editor() which
3944 handles the complexities inherent to situations where a given
3945 directory's children may have intersecting merges (because they
3946 meet one or more of the criteria described in get_mergeinfo_paths()).
3948 static svn_error_t *
3949 do_directory_merge(const char *url1,
3950 svn_revnum_t revision1,
3951 const char *url2,
3952 svn_revnum_t revision2,
3953 const svn_wc_entry_t *parent_entry,
3954 svn_wc_adm_access_t *adm_access,
3955 svn_depth_t depth,
3956 notification_receiver_baton_t *notify_b,
3957 merge_cmd_baton_t *merge_b,
3958 apr_pool_t *pool)
3960 svn_error_t *err = SVN_NO_ERROR;
3961 apr_array_header_t *children_with_mergeinfo;
3962 int merge_target_len = strlen(merge_b->target);
3963 int i;
3964 svn_merge_range_t range;
3965 svn_ra_session_t *ra_session;
3966 svn_boolean_t inheritable;
3967 apr_pool_t *iterpool;
3968 const char *target_wcpath = svn_wc_adm_access_path(adm_access);
3969 svn_client__merge_path_t *target_merge_path;
3970 svn_boolean_t is_rollback = (revision1 > revision2);
3971 const char *primary_url = is_rollback ? url1 : url2;
3972 const char *source_root_url, *mergeinfo_path;
3973 svn_boolean_t honor_mergeinfo, record_mergeinfo;
3974 svn_boolean_t same_urls = (strcmp(url1, url2) == 0);
3976 mergeinfo_behavior(&honor_mergeinfo, &record_mergeinfo, merge_b);
3978 /* Initialize CHILDREN_WITH_MERGEINFO. */
3979 children_with_mergeinfo =
3980 apr_array_make(pool, 0, sizeof(svn_client__merge_path_t *));
3981 notify_b->children_with_mergeinfo = children_with_mergeinfo;
3983 /* If our merge sources aren't related to each other, or don't come
3984 from the same repository as our target, mergeinfo is meaningless
3985 and we can skip right to the business of merging changes! We'll
3986 just drop a dummy item into CHILDREN_WITH_MERGEINFO if the merge
3987 sources are related. */
3988 if (! (merge_b->sources_ancestral && merge_b->same_repos))
3990 if (merge_b->sources_ancestral)
3992 svn_client__merge_path_t *item = apr_pcalloc(pool, sizeof(*item));
3993 svn_merge_range_t *itemrange = apr_pcalloc(pool, sizeof(*itemrange));
3994 apr_array_header_t *remaining_ranges =
3995 apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
3997 itemrange->start = revision1;
3998 itemrange->end = revision2;
3999 itemrange->inheritable = TRUE;
4000 APR_ARRAY_PUSH(remaining_ranges, svn_merge_range_t *) = itemrange;
4002 item->path = apr_pstrdup(pool, target_wcpath);
4003 item->remaining_ranges = remaining_ranges;
4004 APR_ARRAY_PUSH(children_with_mergeinfo,
4005 svn_client__merge_path_t *) = item;
4007 return drive_merge_report_editor(target_wcpath,
4008 url1, revision1, url2, revision2,
4009 NULL, is_rollback, depth, notify_b,
4010 adm_access, &merge_callbacks,
4011 merge_b, pool);
4014 /*** If we get here, we're dealing with related sources from the
4015 same repository as the target -- merge tracking might be
4016 happenin'! ***/
4018 /* Point our RA_SESSION to the URL of our youngest merge source side. */
4019 ra_session = is_rollback ? merge_b->ra_session1 : merge_b->ra_session2;
4021 /* Fill CHILDREN_WITH_MERGEINFO with child paths (const
4022 svn_client__merge_path_t *) which might have intersecting merges
4023 because they meet one or more of the criteria described in
4024 get_mergeinfo_paths(). Here the paths are arranged in a depth
4025 first order. */
4026 SVN_ERR(svn_ra_get_repos_root(ra_session, &source_root_url, pool));
4027 SVN_ERR(svn_client__path_relative_to_root(&mergeinfo_path, primary_url,
4028 source_root_url, TRUE, NULL,
4029 NULL, pool));
4030 SVN_ERR(get_mergeinfo_paths(children_with_mergeinfo, merge_b,
4031 mergeinfo_path, parent_entry, source_root_url,
4032 url1, url2, revision1, revision2,
4033 ra_session, adm_access,
4034 merge_b->ctx, depth, pool));
4036 /* The first item from the CHILDREN_WITH_MERGEINFO is the target
4037 thanks to depth-first ordering. */
4038 target_merge_path = APR_ARRAY_IDX(children_with_mergeinfo, 0,
4039 svn_client__merge_path_t *);
4040 merge_b->target_missing_child = target_merge_path->missing_child;
4041 inheritable = ((! merge_b->target_missing_child)
4042 && ((depth == svn_depth_infinity)
4043 || (depth == svn_depth_immediates)));
4045 /* If we are honoring mergeinfo, then for each item in
4046 CHILDREN_WITH_MERGEINFO, we need to calculate what needs to be
4047 merged, and then merge it. Otherwise, we just merge what we were
4048 asked to merge across the whole tree. */
4049 SVN_ERR(populate_remaining_ranges(children_with_mergeinfo,
4050 source_root_url,
4051 url1, revision1, url2, revision2,
4052 inheritable, honor_mergeinfo,
4053 ra_session, mergeinfo_path,
4054 adm_access, merge_b));
4056 if (honor_mergeinfo)
4058 svn_revnum_t start_rev, end_rev;
4060 /* From the remaining ranges of each item in
4061 CHILDREN_WITH_MERGEINFO, pick the most inclusive start and
4062 end revisions. */
4063 start_rev = get_most_inclusive_start_rev(children_with_mergeinfo,
4064 is_rollback);
4065 if (start_rev == SVN_INVALID_REVNUM)
4066 start_rev = revision1;
4068 end_rev = get_youngest_end_rev(children_with_mergeinfo, is_rollback);
4070 /* Build a range which describes our most inclusive merge. */
4071 range.start = start_rev;
4072 range.end = revision2;
4073 range.inheritable = inheritable;
4075 /* While END_REV is valid, do the following:
4077 1. slice each remaining ranges around this 'end_rev'.
4078 2. starting with START_REV, call
4079 drive_merge_report_editor() on MERGE_B->target for
4080 start_rev:end_rev.
4081 3. remove the first item from each remaining range.
4082 4. set START_REV=END_REV and pick the next END_REV.
4083 5. lather, rinse, repeat.
4085 iterpool = svn_pool_create(pool);
4086 while (end_rev != SVN_INVALID_REVNUM)
4088 svn_revnum_t next_end_rev;
4089 const char *real_url1 = url1, *real_url2 = url2;
4090 const char *old_sess1_url = NULL, *old_sess2_url = NULL;
4092 svn_pool_clear(iterpool);
4094 /* Use persistent pool while playing with remaining_ranges. */
4095 slice_remaining_ranges(children_with_mergeinfo, is_rollback,
4096 end_rev, pool);
4097 notify_b->cur_ancestor_index = -1;
4099 /* URL1@REVISION1 is a real location; URL2@REVISION2 is a
4100 real location -- that much we know (thanks to the merge
4101 source normalization code). But for revisions between
4102 them, the URLs might differ. Here are the rules:
4104 * If URL1 == URL2, then all URLs between REVISION1 and
4105 REVISION2 also match URL1/URL2.
4107 * If URL1 != URL2, then:
4109 * If REVISION1 < REVISION2, only REVISION1 maps to
4110 URL1. The revisions between REVISION1+1 and
4111 REVISION2 (inclusive) map to URL2.
4113 * If REVISION1 > REVISION2, Only REVISION2 maps to
4114 URL2. The revisions between REVISION1 and
4115 REVISION2+1 (inclusive) map to URL1.
4117 We need to adjust our URLs accordingly, here.
4119 if (! same_urls)
4121 if (is_rollback && (end_rev != revision2))
4123 real_url2 = url1;
4124 SVN_ERR(svn_client__ensure_ra_session_url
4125 (&old_sess2_url, merge_b->ra_session2,
4126 real_url2, iterpool));
4128 if ((! is_rollback) && (start_rev != revision1))
4130 real_url1 = url2;
4131 SVN_ERR(svn_client__ensure_ra_session_url
4132 (&old_sess1_url, merge_b->ra_session1,
4133 real_url1, iterpool));
4136 SVN_ERR(drive_merge_report_editor(merge_b->target,
4137 real_url1, start_rev, real_url2,
4138 end_rev, children_with_mergeinfo,
4139 is_rollback,
4140 depth, notify_b, adm_access,
4141 &merge_callbacks, merge_b,
4142 iterpool));
4143 if (old_sess1_url)
4144 SVN_ERR(svn_ra_reparent(merge_b->ra_session1,
4145 old_sess1_url, iterpool));
4146 if (old_sess2_url)
4147 SVN_ERR(svn_ra_reparent(merge_b->ra_session2,
4148 old_sess2_url, iterpool));
4150 /* Prepare for the next iteration (if any). */
4151 remove_first_range_from_remaining_ranges(children_with_mergeinfo,
4152 pool);
4153 next_end_rev = get_youngest_end_rev(children_with_mergeinfo,
4154 is_rollback);
4155 if ((next_end_rev != SVN_INVALID_REVNUM)
4156 && is_path_conflicted_by_merge(merge_b))
4158 svn_merge_range_t conflicted_range;
4159 conflicted_range.start = start_rev;
4160 conflicted_range.end = end_rev;
4161 err = make_merge_conflict_error(merge_b->target,
4162 &conflicted_range, pool);
4163 range.end = end_rev;
4164 break;
4166 start_rev = end_rev;
4167 end_rev = next_end_rev;
4169 svn_pool_destroy(iterpool);
4171 else
4173 /* Build a range which describes our most inclusive merge. */
4174 range.start = revision1;
4175 range.end = revision2;
4176 range.inheritable = inheritable;
4178 SVN_ERR(drive_merge_report_editor(merge_b->target,
4179 url1, revision1, url2, revision2,
4180 NULL, is_rollback,
4181 depth, notify_b, adm_access,
4182 &merge_callbacks, merge_b,
4183 pool));
4186 /* Record mergeinfo where appropriate.
4188 NOTE: any paths in CHILDREN_WITH_MERGEINFO which were switched
4189 but had no explicit working mergeinfo at the start of the call,
4190 will have some at the end of it if merge is not a no-op merge.
4192 iterpool = svn_pool_create(pool);
4193 if (record_mergeinfo)
4195 /* Update the WC mergeinfo here to account for our new
4196 merges, minus any unresolved conflicts and skips. */
4197 apr_hash_t *merges;
4199 /* Remove absent children at or under TARGET_WCPATH from
4200 NOTIFY_B->SKIPPED_PATHS and CHILDREN_WITH_MERGEINFO before we
4201 calculate the merges performed. */
4202 remove_absent_children(merge_b->target,
4203 children_with_mergeinfo, notify_b);
4204 SVN_ERR(determine_merges_performed(&merges, merge_b->target, &range,
4205 depth, adm_access, notify_b, merge_b,
4206 iterpool));
4207 if (! merge_b->operative_merge)
4209 if (merge_b->override_set)
4211 /* get_mergeinfo_paths() may have made some mergeinfo
4212 modifications that must be removed if this is a
4213 no-op merge. */
4214 for (i = 0; i < children_with_mergeinfo->nelts; i++)
4216 svn_client__merge_path_t *child =
4217 APR_ARRAY_IDX(children_with_mergeinfo,
4218 i, svn_client__merge_path_t *);
4219 if (child)
4220 SVN_ERR(svn_wc_prop_set2(SVN_PROP_MERGEINFO,
4221 child->propval, child->path,
4222 adm_access, TRUE, iterpool));
4225 svn_pool_destroy(iterpool);
4226 return err;
4228 SVN_ERR(record_mergeinfo_on_merged_children(depth, adm_access, notify_b,
4229 merge_b, iterpool));
4230 SVN_ERR(update_wc_mergeinfo(merge_b->target, parent_entry,
4231 mergeinfo_path, merges,
4232 is_rollback, adm_access, merge_b->ctx,
4233 iterpool));
4234 for (i = 0; i < children_with_mergeinfo->nelts; i++)
4236 const char *child_repos_path;
4237 const char *child_merge_src_canon_path;
4238 const svn_wc_entry_t *child_entry;
4239 svn_client__merge_path_t *child =
4240 APR_ARRAY_IDX(children_with_mergeinfo, i,
4241 svn_client__merge_path_t *);
4242 if (!child || child->absent)
4243 continue;
4245 if (strlen(child->path) == merge_target_len)
4246 child_repos_path = "";
4247 else
4248 child_repos_path = child->path +
4249 (merge_target_len ? merge_target_len + 1 : 0);
4250 child_merge_src_canon_path = svn_path_join(mergeinfo_path,
4251 child_repos_path,
4252 iterpool);
4253 SVN_ERR(svn_wc__entry_versioned(&child_entry, child->path,
4254 adm_access, FALSE, iterpool));
4256 if (merge_b->operative_merge)
4258 apr_array_header_t *child_merge_rangelist;
4259 svn_merge_range_t *child_merge_range;
4260 apr_hash_t *child_merges = apr_hash_make(iterpool);
4261 child_merge_range = svn_merge_range_dup(&range, iterpool);
4262 if (child_entry->kind == svn_node_file)
4263 child_merge_range->inheritable = TRUE;
4264 else
4265 child_merge_range->inheritable =
4266 (!(child->missing_child)
4267 && (depth == svn_depth_infinity
4268 || depth == svn_depth_immediates));
4269 child_merge_rangelist =
4270 apr_array_make(iterpool, 1,
4271 sizeof(child_merge_range));
4272 APR_ARRAY_PUSH(child_merge_rangelist,
4273 svn_merge_range_t *) = child_merge_range;
4274 apr_hash_set(child_merges, child->path, APR_HASH_KEY_STRING,
4275 child_merge_rangelist);
4276 /* If merge target has indirect mergeinfo set it before
4277 recording the first merge range. */
4278 if (child->indirect_mergeinfo)
4280 SVN_ERR(svn_client__record_wc_mergeinfo(
4281 child->path,
4282 child->pre_merge_mergeinfo,
4283 adm_access,
4284 iterpool));
4286 SVN_ERR(update_wc_mergeinfo(child->path, child_entry,
4287 child_merge_src_canon_path,
4288 child_merges, is_rollback,
4289 adm_access, merge_b->ctx, iterpool));
4291 SVN_ERR(mark_mergeinfo_as_inheritable_for_a_range(
4292 child->pre_merge_mergeinfo,
4293 TRUE,
4294 &range,
4295 child_merge_src_canon_path,
4296 child->path,
4297 adm_access,
4298 merge_b,
4299 children_with_mergeinfo,
4300 i, iterpool));
4301 if (i > 0)
4302 SVN_ERR(svn_client__elide_mergeinfo(child->path, merge_b->target,
4303 child_entry, adm_access,
4304 merge_b->ctx, iterpool));
4305 } /* (i = 0; i < children_with_mergeinfo->nelts; i++) */
4306 } /* (!merge_b->dry_run && merge_b->same_repos) */
4308 svn_pool_destroy(iterpool);
4309 return err;
4313 /* Drive a merge of MERGE_SOURCES into working copy path TARGET (with
4314 associated TARGET_ENTRY and ADM_ACCESS baton).
4316 If SOURCES_ANCESTRAL is set, then for every merge source in
4317 MERGE_SOURCES, the "left" and "right" side of the merge source are
4318 ancestrally related. (See 'MERGEINFO MERGE SOURCE NORMALIZATION'
4319 for more on what that means and how it matters.)
4321 If SOURCES_RELATED is set, the "left" and "right" sides of the
4322 merge source are historically related (ancestors, uncles, second
4323 cousins thrice removed, etc...). (This is passed through to
4324 do_file_merge() to simulate the history checks that the repository
4325 logic does in the directory case.)
4327 SAME_REPOS is TRUE iff the merge sources live in the same
4328 repository as the one from which the target working copy has been
4329 checked out.
4331 FORCE, DRY_RUN, RECORD_ONLY, IGNORE_ANCESTRY, DEPTH, MERGE_OPTIONS,
4332 and CTX are as described in the docstring for svn_client_merge_peg3().
4334 static svn_error_t *
4335 do_merge(apr_array_header_t *merge_sources,
4336 const char *target,
4337 const svn_wc_entry_t *target_entry,
4338 svn_wc_adm_access_t *adm_access,
4339 svn_boolean_t sources_ancestral,
4340 svn_boolean_t sources_related,
4341 svn_boolean_t same_repos,
4342 svn_boolean_t ignore_ancestry,
4343 svn_boolean_t force,
4344 svn_boolean_t dry_run,
4345 svn_boolean_t record_only,
4346 svn_depth_t depth,
4347 const apr_array_header_t *merge_options,
4348 svn_client_ctx_t *ctx,
4349 apr_pool_t *pool)
4351 apr_pool_t *subpool = svn_pool_create(pool);
4352 merge_cmd_baton_t merge_cmd_baton;
4353 notification_receiver_baton_t notify_baton;
4354 svn_config_t *cfg;
4355 const char *diff3_cmd;
4356 int i;
4357 svn_boolean_t checked_mergeinfo_capability = FALSE;
4359 /* If this is a dry-run record-only merge, there's nothing to do. */
4360 if (record_only && dry_run)
4361 return SVN_NO_ERROR;
4363 /* Sanity check: we can do a record-only merge (which is a
4364 merge-tracking thing) if the sources aren't related, because we
4365 don't do merge-tracking if the sources aren't related. */
4366 if (record_only && (! sources_ancestral))
4367 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
4368 _("Use of two URLs is not compatible with "
4369 "mergeinfo modification"));
4371 /* Ensure a known depth. */
4372 if (depth == svn_depth_unknown)
4373 depth = target_entry->depth;
4375 /* Set up the diff3 command, so various callers don't have to. */
4376 cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
4377 APR_HASH_KEY_STRING) : NULL;
4378 svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS,
4379 SVN_CONFIG_OPTION_DIFF3_CMD, NULL);
4382 /* Build the merge context baton (or at least the parts of it that
4383 don't need to be reset for each merge source). */
4384 merge_cmd_baton.force = force;
4385 merge_cmd_baton.dry_run = dry_run;
4386 merge_cmd_baton.record_only = record_only;
4387 merge_cmd_baton.ignore_ancestry = ignore_ancestry;
4388 merge_cmd_baton.same_repos = same_repos;
4389 merge_cmd_baton.mergeinfo_capable = FALSE;
4390 merge_cmd_baton.sources_ancestral = sources_ancestral;
4391 merge_cmd_baton.ctx = ctx;
4392 merge_cmd_baton.target_missing_child = FALSE;
4393 merge_cmd_baton.target = target;
4394 merge_cmd_baton.pool = subpool;
4395 merge_cmd_baton.merge_options = merge_options;
4396 merge_cmd_baton.diff3_cmd = diff3_cmd;
4398 /* Build the notification receiver baton. */
4399 notify_baton.wrapped_func = ctx->notify_func2;
4400 notify_baton.wrapped_baton = ctx->notify_baton2;
4401 notify_baton.nbr_notifications = 0;
4402 notify_baton.nbr_operative_notifications = 0;
4403 notify_baton.merged_paths = NULL;
4404 notify_baton.skipped_paths = NULL;
4405 notify_baton.is_single_file_merge = FALSE;
4406 notify_baton.children_with_mergeinfo = NULL;
4407 notify_baton.cur_ancestor_index = -1;
4408 notify_baton.merge_b = &merge_cmd_baton;
4409 notify_baton.pool = pool;
4411 for (i = 0; i < merge_sources->nelts; i++)
4413 merge_source_t *merge_source =
4414 APR_ARRAY_IDX(merge_sources, i, merge_source_t *);
4415 const char *url1, *url2;
4416 svn_revnum_t rev1, rev2;
4417 svn_ra_session_t *ra_session1, *ra_session2;
4419 svn_pool_clear(subpool);
4421 /* Convenience variables. */
4422 url1 = merge_source->url1;
4423 url2 = merge_source->url2;
4424 rev1 = merge_source->rev1;
4425 rev2 = merge_source->rev2;
4427 /* Sanity check: if our left- and right-side merge sources are
4428 the same, there's nothing to here. */
4429 if ((strcmp(url1, url2) == 0) && (rev1 == rev2))
4430 continue;
4432 /* Establish RA sessions to our URLs. */
4433 SVN_ERR(svn_client__open_ra_session_internal(&ra_session1, url1,
4434 NULL, NULL, NULL,
4435 FALSE, TRUE, ctx, subpool));
4436 SVN_ERR(svn_client__open_ra_session_internal(&ra_session2, url2,
4437 NULL, NULL, NULL,
4438 FALSE, TRUE, ctx, subpool));
4440 /* Populate the portions of the merge context baton that need to
4441 be reset for each merge source iteration. */
4442 merge_cmd_baton.url = url2;
4443 merge_cmd_baton.added_path = NULL;
4444 merge_cmd_baton.add_necessitated_merge = FALSE;
4445 merge_cmd_baton.dry_run_deletions =
4446 dry_run ? apr_hash_make(subpool) : NULL;
4447 merge_cmd_baton.conflicted_paths = NULL;
4448 merge_cmd_baton.operative_merge = FALSE;
4449 merge_cmd_baton.target_has_dummy_merge_range = FALSE;
4450 merge_cmd_baton.override_set = FALSE;
4451 merge_cmd_baton.ra_session1 = ra_session1;
4452 merge_cmd_baton.ra_session2 = ra_session2;
4454 /* Populate the portions of the merge context baton that require
4455 an RA session to set, but shouldn't be reset for each iteration. */
4456 if (! checked_mergeinfo_capability)
4458 SVN_ERR(svn_ra_has_capability(ra_session1,
4459 &merge_cmd_baton.mergeinfo_capable,
4460 SVN_RA_CAPABILITY_MERGEINFO, subpool));
4461 checked_mergeinfo_capability = TRUE;
4464 /* If this is a record-only merge and our sources are from the
4465 same repository as our target, just do the record and move on. */
4466 if (same_repos && record_only)
4468 const char *merge_source_url = (rev1 < rev2) ? url2 : url1;
4469 svn_merge_range_t range;
4470 range.start = rev1;
4471 range.end = rev2;
4472 range.inheritable = TRUE;
4473 SVN_ERR(record_mergeinfo_for_record_only_merge(merge_source_url,
4474 &range,
4475 target_entry,
4476 adm_access,
4477 &merge_cmd_baton,
4478 subpool));
4480 continue;
4483 /* Call our merge helpers based on entry kind. */
4484 if (target_entry->kind == svn_node_file)
4486 SVN_ERR(do_file_merge(url1, rev1, url2, rev2, target,
4487 sources_related, adm_access,
4488 &notify_baton, &merge_cmd_baton, subpool));
4490 else if (target_entry->kind == svn_node_dir)
4492 SVN_ERR(do_directory_merge(url1, rev1, url2, rev2, target_entry,
4493 adm_access, depth, &notify_baton,
4494 &merge_cmd_baton, subpool));
4497 /* The final mergeinfo on TARGET_WCPATH may itself elide. */
4498 if ((! dry_run) && merge_cmd_baton.operative_merge)
4499 SVN_ERR(svn_client__elide_mergeinfo(target, NULL, target_entry,
4500 adm_access, ctx, subpool));
4503 svn_pool_destroy(subpool);
4504 return SVN_NO_ERROR;
4508 /*-----------------------------------------------------------------------*/
4510 /*** Public APIs ***/
4512 svn_error_t *
4513 svn_client_merge3(const char *source1,
4514 const svn_opt_revision_t *revision1,
4515 const char *source2,
4516 const svn_opt_revision_t *revision2,
4517 const char *target_wcpath,
4518 svn_depth_t depth,
4519 svn_boolean_t ignore_ancestry,
4520 svn_boolean_t force,
4521 svn_boolean_t record_only,
4522 svn_boolean_t dry_run,
4523 const apr_array_header_t *merge_options,
4524 svn_client_ctx_t *ctx,
4525 apr_pool_t *pool)
4527 svn_wc_adm_access_t *adm_access;
4528 const svn_wc_entry_t *entry;
4529 const char *URL1, *URL2;
4530 svn_revnum_t rev1, rev2;
4531 svn_boolean_t related = FALSE, ancestral = FALSE;
4532 const char *wc_repos_root, *source_repos_root;
4533 svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
4534 svn_ra_session_t *ra_session1, *ra_session2;
4535 apr_array_header_t *merge_sources;
4536 merge_source_t *merge_source;
4537 svn_opt_revision_t working_rev;
4538 const char *yc_path = NULL;
4539 svn_revnum_t yc_rev = SVN_INVALID_REVNUM;
4540 apr_pool_t *sesspool;
4541 svn_boolean_t same_repos;
4543 /* Sanity check our input -- we require specified revisions. */
4544 if ((revision1->kind == svn_opt_revision_unspecified)
4545 || (revision2->kind == svn_opt_revision_unspecified))
4546 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
4547 _("Not all required revisions are specified"));
4549 /* ### FIXME: This function really ought to do a history check on
4550 the left and right sides of the merge source, and -- if one is an
4551 ancestor of the other -- just call svn_client_merge_peg3() with
4552 the appropriate args. */
4554 /* If source1 or source2 are paths, we need to get the underlying
4555 URL from the wc and save the initial path we were passed so we
4556 can use it as a path parameter (either in the baton or not).
4557 otherwise, the path will just be NULL, which means we won't be
4558 able to figure out some kind of revision specifications, but in
4559 that case it won't matter, because those ways of specifying a
4560 revision are meaningless for a url. */
4561 SVN_ERR(svn_client_url_from_path(&URL1, source1, pool));
4562 if (! URL1)
4563 return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
4564 _("'%s' has no URL"),
4565 svn_path_local_style(source1, pool));
4567 SVN_ERR(svn_client_url_from_path(&URL2, source2, pool));
4568 if (! URL2)
4569 return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
4570 _("'%s' has no URL"),
4571 svn_path_local_style(source2, pool));
4573 /* Open an admistrative session with the working copy. */
4574 SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, target_wcpath,
4575 ! dry_run, -1, ctx->cancel_func,
4576 ctx->cancel_baton, pool));
4578 /* Fetch the target's entry. */
4579 SVN_ERR(svn_wc__entry_versioned(&entry, target_wcpath, adm_access,
4580 FALSE, pool));
4582 /* Determine the working copy target's repository root URL. */
4583 working_rev.kind = svn_opt_revision_working;
4584 SVN_ERR(svn_client__get_repos_root(&wc_repos_root, target_wcpath,
4585 &working_rev, adm_access, ctx, pool));
4587 /* Open some RA sessions to our merge source sides. */
4588 sesspool = svn_pool_create(pool);
4589 SVN_ERR(svn_client__open_ra_session_internal(&ra_session1,
4590 URL1, NULL, NULL, NULL,
4591 FALSE, TRUE, ctx, sesspool));
4592 SVN_ERR(svn_client__open_ra_session_internal(&ra_session2,
4593 URL2, NULL, NULL, NULL,
4594 FALSE, TRUE, ctx, sesspool));
4596 /* Resolve revisions to real numbers. */
4597 SVN_ERR(svn_client__get_revision_number(&rev1, &youngest_rev, ra_session1,
4598 revision1, NULL, sesspool));
4599 SVN_ERR(svn_client__get_revision_number(&rev2, &youngest_rev, ra_session2,
4600 revision2, NULL, sesspool));
4602 /* Get the repository root URL from one of our sessions (the other
4603 doesn't matter -- if it ain't the same, other stuff would fall
4604 over later). We have to dup this into our pool, since the API
4605 declares that this data will live in the session's pool. */
4606 SVN_ERR(svn_ra_get_repos_root(ra_session1, &source_repos_root, sesspool));
4607 source_repos_root = apr_pstrdup(pool, source_repos_root);
4609 /* Do our working copy and sources come from the same repository? */
4610 same_repos = (strcmp(source_repos_root, wc_repos_root) == 0) ? TRUE : FALSE;
4612 /* Unless we're ignoring ancestry, see if the two sources are related. */
4613 if (! ignore_ancestry)
4615 svn_opt_revision_t opt_rev1, opt_rev2;
4616 opt_rev1.kind = opt_rev2.kind = svn_opt_revision_number;
4617 opt_rev1.value.number = rev1;
4618 opt_rev2.value.number = rev2;
4619 SVN_ERR(svn_client__get_youngest_common_ancestor(&yc_path, &yc_rev,
4620 URL1, &opt_rev1,
4621 URL2, &opt_rev2,
4622 ctx, pool));
4625 /* Check for a youngest common ancestor. If we have one, we'll be
4626 doing merge tracking.
4628 So, given a requested merge of the differences between A and
4629 B, and a common ancestor of C, we will find ourselves in one of
4630 four positions, and four different approaches:
4632 A == B == C there's nothing to merge
4634 A == C != B we merge the changes between A (or C) and B
4636 B == C != A we merge the changes between B (or C) and A
4638 A != B != C we merge the changes between A and B without
4639 merge recording, then record-only two merges:
4640 from A to C, and from C to B
4642 if (yc_path && SVN_IS_VALID_REVNUM(yc_rev))
4644 apr_array_header_t *ranges;
4645 svn_opt_revision_range_t *range;
4646 svn_opt_revision_t peg_revision;
4647 peg_revision.kind = svn_opt_revision_number;
4649 /* Note that our merge sources are related. */
4650 related = TRUE;
4652 /* Make YC_PATH into a full URL. */
4653 yc_path = svn_path_join(source_repos_root,
4654 svn_path_uri_encode(yc_path, pool), pool);
4656 /* If the common ancestor matches the right side of our merge,
4657 then we only need to reverse-merge the left side. */
4658 if ((strcmp(yc_path, URL2) == 0) && (yc_rev == rev2))
4660 ancestral = TRUE;
4661 range = apr_pcalloc(pool, sizeof(*range));
4662 range->start.kind = svn_opt_revision_number;
4663 range->start.value.number = rev1;
4664 range->end.kind = svn_opt_revision_number;
4665 range->end.value.number = yc_rev;
4666 ranges = apr_array_make(pool, 2, sizeof(svn_opt_revision_range_t *));
4667 APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
4668 peg_revision.value.number = rev1;
4669 SVN_ERR(normalize_merge_sources(&merge_sources, URL1, URL1,
4670 source_repos_root, &peg_revision,
4671 ranges, ra_session1, ctx, pool));
4673 /* If the common ancestor matches the left side of our merge,
4674 then we only need to merge the right side. */
4675 else if ((strcmp(yc_path, URL1) == 0) && (yc_rev == rev1))
4677 ancestral = TRUE;
4678 range = apr_pcalloc(pool, sizeof(*range));
4679 range->start.kind = svn_opt_revision_number;
4680 range->start.value.number = yc_rev;
4681 range->end.kind = svn_opt_revision_number;
4682 range->end.value.number = rev2;
4683 ranges = apr_array_make(pool, 2, sizeof(svn_opt_revision_range_t *));
4684 APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
4685 peg_revision.value.number = rev2;
4686 SVN_ERR(normalize_merge_sources(&merge_sources, URL2, URL2,
4687 source_repos_root, &peg_revision,
4688 ranges, ra_session2, ctx, pool));
4690 /* And otherwise, we need to do both: reverse merge the left
4691 side, and merge the right. */
4692 else
4694 apr_array_header_t *remove_sources, *add_sources;
4696 range = apr_pcalloc(pool, sizeof(*range));
4697 range->start.kind = svn_opt_revision_number;
4698 range->start.value.number = rev1;
4699 range->end.kind = svn_opt_revision_number;
4700 range->end.value.number = yc_rev;
4701 ranges = apr_array_make(pool, 2, sizeof(svn_opt_revision_range_t *));
4702 APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
4703 peg_revision.value.number = rev1;
4704 SVN_ERR(normalize_merge_sources(&remove_sources, URL1, URL1,
4705 source_repos_root, &peg_revision,
4706 ranges, ra_session1, ctx, pool));
4708 range = apr_pcalloc(pool, sizeof(*range));
4709 range->start.kind = svn_opt_revision_number;
4710 range->start.value.number = yc_rev;
4711 range->end.kind = svn_opt_revision_number;
4712 range->end.value.number = rev2;
4713 ranges = apr_array_make(pool, 2, sizeof(svn_opt_revision_range_t *));
4714 APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
4715 peg_revision.value.number = rev2;
4716 SVN_ERR(normalize_merge_sources(&add_sources, URL2, URL2,
4717 source_repos_root, &peg_revision,
4718 ranges, ra_session2, ctx, pool));
4720 /* Close our temporary RA sessions. */
4721 svn_pool_destroy(sesspool);
4723 /* If this isn't a record-only merge, we'll first do a stupid
4724 point-to-point merge... */
4725 if (! record_only)
4727 merge_source_t *faux_source;
4728 apr_array_header_t *faux_sources =
4729 apr_array_make(pool, 1, sizeof(merge_source_t *));
4730 faux_source = apr_pcalloc(pool, sizeof(*faux_source));
4731 faux_source->url1 = URL1;
4732 faux_source->url2 = URL2;
4733 faux_source->rev1 = rev1;
4734 faux_source->rev2 = rev2;
4735 APR_ARRAY_PUSH(faux_sources, merge_source_t *) = faux_source;
4736 SVN_ERR(do_merge(faux_sources, target_wcpath, entry, adm_access,
4737 ancestral, related, same_repos,
4738 ignore_ancestry, force, dry_run,
4739 record_only, depth, merge_options, ctx, pool));
4741 /* ... and now we do a pair of record-only merges using the real
4742 sources we've calculated. (We know that each tong in our
4743 fork of our merge source history tree has an ancestral
4744 relationship with the common ancestral, so we force
4745 ancestral=TRUE here.) */
4746 SVN_ERR(do_merge(add_sources, target_wcpath, entry, adm_access,
4747 TRUE, related, same_repos,
4748 ignore_ancestry, force, dry_run,
4749 TRUE, depth, merge_options, ctx, pool));
4750 SVN_ERR(do_merge(remove_sources, target_wcpath, entry, adm_access,
4751 TRUE, related, same_repos,
4752 ignore_ancestry, force, dry_run,
4753 TRUE, depth, merge_options, ctx, pool));
4755 SVN_ERR(svn_wc_adm_close(adm_access));
4756 return SVN_NO_ERROR;
4759 else
4761 /* Build a single-item merge_source_t array. */
4762 merge_sources = apr_array_make(pool, 1, sizeof(merge_source_t *));
4763 merge_source = apr_pcalloc(pool, sizeof(*merge_source));
4764 merge_source->url1 = URL1;
4765 merge_source->url2 = URL2;
4766 merge_source->rev1 = rev1;
4767 merge_source->rev2 = rev2;
4768 APR_ARRAY_PUSH(merge_sources, merge_source_t *) = merge_source;
4771 /* Close our temporary RA sessions. */
4772 svn_pool_destroy(sesspool);
4774 SVN_ERR(do_merge(merge_sources, target_wcpath, entry, adm_access,
4775 ancestral, related, same_repos,
4776 ignore_ancestry, force, dry_run,
4777 record_only, depth, merge_options, ctx, pool));
4779 SVN_ERR(svn_wc_adm_close(adm_access));
4781 return SVN_NO_ERROR;
4784 svn_error_t *
4785 svn_client_merge2(const char *source1,
4786 const svn_opt_revision_t *revision1,
4787 const char *source2,
4788 const svn_opt_revision_t *revision2,
4789 const char *target_wcpath,
4790 svn_boolean_t recurse,
4791 svn_boolean_t ignore_ancestry,
4792 svn_boolean_t force,
4793 svn_boolean_t dry_run,
4794 const apr_array_header_t *merge_options,
4795 svn_client_ctx_t *ctx,
4796 apr_pool_t *pool)
4798 return svn_client_merge3(source1, revision1, source2, revision2,
4799 target_wcpath,
4800 SVN_DEPTH_INFINITY_OR_FILES(recurse),
4801 ignore_ancestry, force, FALSE, dry_run,
4802 merge_options, ctx, pool);
4805 svn_error_t *
4806 svn_client_merge(const char *source1,
4807 const svn_opt_revision_t *revision1,
4808 const char *source2,
4809 const svn_opt_revision_t *revision2,
4810 const char *target_wcpath,
4811 svn_boolean_t recurse,
4812 svn_boolean_t ignore_ancestry,
4813 svn_boolean_t force,
4814 svn_boolean_t dry_run,
4815 svn_client_ctx_t *ctx,
4816 apr_pool_t *pool)
4818 return svn_client_merge2(source1, revision1, source2, revision2,
4819 target_wcpath, recurse, ignore_ancestry, force,
4820 dry_run, NULL, ctx, pool);
4824 svn_error_t *
4825 svn_client_merge_peg3(const char *source,
4826 const apr_array_header_t *ranges_to_merge,
4827 const svn_opt_revision_t *peg_revision,
4828 const char *target_wcpath,
4829 svn_depth_t depth,
4830 svn_boolean_t ignore_ancestry,
4831 svn_boolean_t force,
4832 svn_boolean_t record_only,
4833 svn_boolean_t dry_run,
4834 const apr_array_header_t *merge_options,
4835 svn_client_ctx_t *ctx,
4836 apr_pool_t *pool)
4838 svn_wc_adm_access_t *adm_access;
4839 const svn_wc_entry_t *entry;
4840 const char *URL;
4841 apr_array_header_t *merge_sources;
4842 const char *wc_repos_root, *source_repos_root;
4843 svn_opt_revision_t working_rev;
4844 svn_ra_session_t *ra_session;
4845 apr_pool_t *sesspool;
4847 /* No ranges to merge? No problem. */
4848 if (ranges_to_merge->nelts == 0)
4849 return SVN_NO_ERROR;
4851 /* Open an admistrative session with the working copy. */
4852 SVN_ERR(svn_wc_adm_probe_open3(&adm_access, NULL, target_wcpath,
4853 (! dry_run), -1, ctx->cancel_func,
4854 ctx->cancel_baton, pool));
4856 /* Fetch the target's entry. */
4857 SVN_ERR(svn_wc__entry_versioned(&entry, target_wcpath, adm_access,
4858 FALSE, pool));
4860 /* Make sure we're dealing with a real URL. */
4861 SVN_ERR(svn_client_url_from_path(&URL, source, pool));
4862 if (! URL)
4863 return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
4864 _("'%s' has no URL"),
4865 svn_path_local_style(source, pool));
4867 /* Determine the working copy target's repository root URL. */
4868 working_rev.kind = svn_opt_revision_working;
4869 SVN_ERR(svn_client__get_repos_root(&wc_repos_root, target_wcpath,
4870 &working_rev, adm_access, ctx, pool));
4872 /* Open an RA session to our source URL, and determine its root URL. */
4873 sesspool = svn_pool_create(pool);
4874 SVN_ERR(svn_client__open_ra_session_internal(&ra_session,
4875 URL, NULL, NULL, NULL,
4876 FALSE, TRUE, ctx, sesspool));
4877 SVN_ERR(svn_ra_get_repos_root(ra_session, &source_repos_root, pool));
4879 /* Normalize our merge sources. */
4880 SVN_ERR(normalize_merge_sources(&merge_sources, source, URL,
4881 source_repos_root, peg_revision,
4882 ranges_to_merge, ra_session, ctx, pool));
4884 /* We're done with our little RA session. */
4885 svn_pool_destroy(sesspool);
4887 /* Do the real merge! (We say with confidence that our merge
4888 sources are both ancestral and related.) */
4889 SVN_ERR(do_merge(merge_sources, target_wcpath, entry, adm_access,
4890 TRUE, TRUE,
4891 (strcmp(wc_repos_root, source_repos_root) == 0),
4892 ignore_ancestry, force, dry_run, record_only, depth,
4893 merge_options, ctx, pool));
4895 /* Shutdown the administrative session. */
4896 SVN_ERR(svn_wc_adm_close(adm_access));
4898 return SVN_NO_ERROR;
4902 svn_error_t *
4903 svn_client_merge_peg2(const char *source,
4904 const svn_opt_revision_t *revision1,
4905 const svn_opt_revision_t *revision2,
4906 const svn_opt_revision_t *peg_revision,
4907 const char *target_wcpath,
4908 svn_boolean_t recurse,
4909 svn_boolean_t ignore_ancestry,
4910 svn_boolean_t force,
4911 svn_boolean_t dry_run,
4912 const apr_array_header_t *merge_options,
4913 svn_client_ctx_t *ctx,
4914 apr_pool_t *pool)
4916 svn_opt_revision_range_t range;
4917 apr_array_header_t *ranges_to_merge =
4918 apr_array_make(pool, 1, sizeof(svn_opt_revision_range_t *));
4920 range.start = *revision1;
4921 range.end = *revision2;
4922 APR_ARRAY_PUSH(ranges_to_merge, svn_opt_revision_range_t *) = &range;
4923 return svn_client_merge_peg3(source, ranges_to_merge,
4924 peg_revision,
4925 target_wcpath,
4926 SVN_DEPTH_INFINITY_OR_FILES(recurse),
4927 ignore_ancestry, force, FALSE, dry_run,
4928 merge_options, ctx, pool);
4931 svn_error_t *
4932 svn_client_merge_peg(const char *source,
4933 const svn_opt_revision_t *revision1,
4934 const svn_opt_revision_t *revision2,
4935 const svn_opt_revision_t *peg_revision,
4936 const char *target_wcpath,
4937 svn_boolean_t recurse,
4938 svn_boolean_t ignore_ancestry,
4939 svn_boolean_t force,
4940 svn_boolean_t dry_run,
4941 svn_client_ctx_t *ctx,
4942 apr_pool_t *pool)
4944 return svn_client_merge_peg2(source, revision1, revision2, peg_revision,
4945 target_wcpath, recurse, ignore_ancestry, force,
4946 dry_run, NULL, ctx, pool);