2 * mergeinfo.c : merge history functions for the libsvn_client library
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 #include <apr_pools.h>
25 #include <apr_strings.h>
27 #include "svn_pools.h"
28 #include "svn_dirent_uri.h"
30 #include "svn_string.h"
32 #include "svn_error.h"
33 #include "svn_error_codes.h"
34 #include "svn_props.h"
35 #include "svn_mergeinfo.h"
36 #include "svn_sorts.h"
38 #include "svn_client.h"
41 #include "private/svn_opt_private.h"
42 #include "private/svn_mergeinfo_private.h"
43 #include "private/svn_wc_private.h"
44 #include "private/svn_ra_private.h"
45 #include "private/svn_fspath.h"
46 #include "private/svn_client_private.h"
48 #include "mergeinfo.h"
49 #include "svn_private_config.h"
53 svn_client__merge_path_t *
54 svn_client__merge_path_dup(const svn_client__merge_path_t *old,
57 svn_client__merge_path_t *new = apr_pmemdup(pool, old, sizeof(*old));
59 new->abspath = apr_pstrdup(pool, old->abspath);
60 if (new->remaining_ranges)
61 new->remaining_ranges = svn_rangelist_dup(old->remaining_ranges, pool);
62 if (new->pre_merge_mergeinfo)
63 new->pre_merge_mergeinfo = svn_mergeinfo_dup(old->pre_merge_mergeinfo,
65 if (new->implicit_mergeinfo)
66 new->implicit_mergeinfo = svn_mergeinfo_dup(old->implicit_mergeinfo,
72 svn_client__merge_path_t *
73 svn_client__merge_path_create(const char *abspath,
76 svn_client__merge_path_t *result = apr_pcalloc(pool, sizeof(*result));
78 result->abspath = apr_pstrdup(pool, abspath);
83 svn_client__parse_mergeinfo(svn_mergeinfo_t *mergeinfo,
84 svn_wc_context_t *wc_ctx,
85 const char *local_abspath,
86 apr_pool_t *result_pool,
87 apr_pool_t *scratch_pool)
89 const svn_string_t *propval;
93 /* ### Use svn_wc_prop_get() would actually be sufficient for now.
94 ### DannyB thinks that later we'll need behavior more like
95 ### svn_client__get_prop_from_wc(). */
96 SVN_ERR(svn_wc_prop_get2(&propval, wc_ctx, local_abspath, SVN_PROP_MERGEINFO,
97 scratch_pool, scratch_pool));
99 SVN_ERR(svn_mergeinfo_parse(mergeinfo, propval->data, result_pool));
105 svn_client__record_wc_mergeinfo(const char *local_abspath,
106 svn_mergeinfo_t mergeinfo,
107 svn_boolean_t do_notification,
108 svn_client_ctx_t *ctx,
109 apr_pool_t *scratch_pool)
111 svn_string_t *mergeinfo_str = NULL;
112 svn_boolean_t mergeinfo_changes = FALSE;
114 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
116 /* Convert MERGEINFO (if any) into text for storage as a property value. */
118 SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_str, mergeinfo, scratch_pool));
120 if (do_notification && ctx->notify_func2)
121 SVN_ERR(svn_client__mergeinfo_status(&mergeinfo_changes, ctx->wc_ctx,
122 local_abspath, scratch_pool));
124 /* Record the new mergeinfo in the WC. */
125 /* ### Later, we'll want behavior more analogous to
126 ### svn_client__get_prop_from_wc(). */
127 SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath, SVN_PROP_MERGEINFO,
128 mergeinfo_str, svn_depth_empty,
129 TRUE /* skip checks */, NULL,
130 NULL, NULL /* cancellation */,
131 NULL, NULL /* notification */,
134 if (do_notification && ctx->notify_func2)
136 svn_wc_notify_t *notify =
137 svn_wc_create_notify(local_abspath,
138 svn_wc_notify_merge_record_info,
140 if (mergeinfo_changes)
141 notify->prop_state = svn_wc_notify_state_merged;
143 notify->prop_state = svn_wc_notify_state_changed;
145 ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
152 svn_client__record_wc_mergeinfo_catalog(apr_hash_t *result_catalog,
153 svn_client_ctx_t *ctx,
154 apr_pool_t *scratch_pool)
156 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
158 if (apr_hash_count(result_catalog))
161 apr_array_header_t *sorted_cat =
162 svn_sort__hash(result_catalog, svn_sort_compare_items_as_paths,
165 /* Write the mergeinfo out in sorted order of the paths (presumably just
166 * so that the notifications are in a predictable, convenient order). */
167 for (i = 0; i < sorted_cat->nelts; i++)
169 svn_sort__item_t elt = APR_ARRAY_IDX(sorted_cat, i,
173 svn_pool_clear(iterpool);
174 err = svn_client__record_wc_mergeinfo(elt.key, elt.value, TRUE,
177 if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
179 /* PATH isn't just missing, it's not even versioned as far
180 as this working copy knows. But it was included in
181 MERGES, which means that the server knows about it.
182 Likely we don't have access to the source due to authz
183 restrictions. For now just clear the error and
185 svn_error_clear(err);
193 svn_pool_destroy(iterpool);
197 /*-----------------------------------------------------------------------*/
199 /*** Retrieving mergeinfo. ***/
202 svn_client__get_wc_mergeinfo(svn_mergeinfo_t *mergeinfo,
203 svn_boolean_t *inherited_p,
204 svn_mergeinfo_inheritance_t inherit,
205 const char *local_abspath,
206 const char *limit_abspath,
207 const char **walked_path,
208 svn_boolean_t ignore_invalid_mergeinfo,
209 svn_client_ctx_t *ctx,
210 apr_pool_t *result_pool,
211 apr_pool_t *scratch_pool)
213 const char *walk_relpath = "";
214 svn_mergeinfo_t wc_mergeinfo;
215 svn_revnum_t base_revision;
216 apr_pool_t *iterpool;
217 svn_boolean_t inherited;
219 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
221 SVN_ERR_ASSERT(svn_dirent_is_absolute(limit_abspath));
223 SVN_ERR(svn_wc__node_get_base(NULL, &base_revision, NULL, NULL, NULL, NULL,
224 ctx->wc_ctx, local_abspath,
225 TRUE /* ignore_enoent */,
226 FALSE /* show_hidden */,
227 scratch_pool, scratch_pool));
229 iterpool = svn_pool_create(scratch_pool);
232 svn_pool_clear(iterpool);
234 /* Don't look for explicit mergeinfo on LOCAL_ABSPATH if we are only
235 interested in inherited mergeinfo. */
236 if (inherit == svn_mergeinfo_nearest_ancestor)
239 inherit = svn_mergeinfo_inherited;
243 /* Look for mergeinfo on LOCAL_ABSPATH. If there isn't any and we
244 want inherited mergeinfo, walk towards the root of the WC until
245 we encounter either (a) an unversioned directory, or
246 (b) mergeinfo. If we encounter (b), use that inherited
247 mergeinfo as our baseline. */
248 svn_error_t *err = svn_client__parse_mergeinfo(&wc_mergeinfo,
253 if ((ignore_invalid_mergeinfo || walk_relpath [0] != '\0')
255 && err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
257 svn_error_clear(err);
258 wc_mergeinfo = apr_hash_make(result_pool);
267 if (wc_mergeinfo == NULL &&
268 inherit != svn_mergeinfo_explicit &&
269 !svn_dirent_is_root(local_abspath, strlen(local_abspath)))
271 svn_boolean_t is_wc_root;
272 svn_boolean_t is_switched;
273 svn_revnum_t parent_base_rev;
274 svn_revnum_t parent_changed_rev;
276 /* Don't look any higher than the limit path. */
277 if (limit_abspath && strcmp(limit_abspath, local_abspath) == 0)
280 /* If we've reached the root of the working copy don't look any
282 SVN_ERR(svn_wc_check_root(&is_wc_root, &is_switched, NULL,
283 ctx->wc_ctx, local_abspath, iterpool));
284 if (is_wc_root || is_switched)
287 /* No explicit mergeinfo on this path. Look higher up the
288 directory tree while keeping track of what we've walked. */
289 walk_relpath = svn_relpath_join(svn_dirent_basename(local_abspath,
291 walk_relpath, result_pool);
292 local_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
294 SVN_ERR(svn_wc__node_get_base(NULL, &parent_base_rev, NULL, NULL,
296 ctx->wc_ctx, local_abspath,
298 scratch_pool, scratch_pool));
300 /* ### This checks the WORKING changed_rev, so invalid on replacement
301 ### not even reliable in case an ancestor was copied from a
302 ### different location */
303 SVN_ERR(svn_wc__node_get_changed_info(&parent_changed_rev,
305 ctx->wc_ctx, local_abspath,
309 /* Look in LOCAL_ABSPATH's parent for inherited mergeinfo if
310 LOCAL_ABSPATH has no base revision because it is an uncommitted
311 addition, or if its base revision falls within the inclusive
312 range of its parent's last changed revision to the parent's base
313 revision; otherwise stop looking for inherited mergeinfo. */
314 if (SVN_IS_VALID_REVNUM(base_revision)
315 && (base_revision < parent_changed_rev
316 || parent_base_rev < base_revision))
319 /* We haven't yet risen above the root of the WC. */
325 svn_pool_destroy(iterpool);
327 if (svn_path_is_empty(walk_relpath))
329 /* Mergeinfo is explicit. */
331 *mergeinfo = wc_mergeinfo;
335 /* Mergeinfo may be inherited. */
339 SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(mergeinfo,
353 *walked_path = walk_relpath;
355 /* Remove non-inheritable mergeinfo and paths mapped to empty ranges
356 which may occur if WCPATH's mergeinfo is not explicit. */
358 && apr_hash_count(*mergeinfo)) /* Nothing to do for empty mergeinfo. */
360 SVN_ERR(svn_mergeinfo_inheritable2(mergeinfo, *mergeinfo, NULL,
361 SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
362 TRUE, result_pool, scratch_pool));
363 svn_mergeinfo__remove_empty_rangelists(*mergeinfo, result_pool);
367 *inherited_p = inherited;
373 svn_client__get_wc_mergeinfo_catalog(svn_mergeinfo_catalog_t *mergeinfo_cat,
374 svn_boolean_t *inherited,
375 svn_boolean_t include_descendants,
376 svn_mergeinfo_inheritance_t inherit,
377 const char *local_abspath,
378 const char *limit_path,
379 const char **walked_path,
380 svn_boolean_t ignore_invalid_mergeinfo,
381 svn_client_ctx_t *ctx,
382 apr_pool_t *result_pool,
383 apr_pool_t *scratch_pool)
385 const char *target_repos_relpath;
386 svn_mergeinfo_t mergeinfo;
387 const char *repos_root;
389 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
390 *mergeinfo_cat = NULL;
391 SVN_ERR(svn_wc__node_get_repos_info(NULL, &target_repos_relpath,
393 ctx->wc_ctx, local_abspath,
394 scratch_pool, scratch_pool));
396 /* Get the mergeinfo for the LOCAL_ABSPATH target and set *INHERITED and
398 SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, inherited, inherit,
399 local_abspath, limit_path,
400 walked_path, ignore_invalid_mergeinfo,
401 ctx, result_pool, scratch_pool));
403 /* Add any explicit/inherited mergeinfo for LOCAL_ABSPATH to
407 *mergeinfo_cat = apr_hash_make(result_pool);
408 svn_hash_sets(*mergeinfo_cat,
409 apr_pstrdup(result_pool, target_repos_relpath), mergeinfo);
412 /* If LOCAL_ABSPATH is a directory and we want the subtree mergeinfo too,
415 With WC-NG it is cheaper to do a single db transaction, than first
416 looking if we really have a directory. */
417 if (include_descendants)
419 apr_hash_t *mergeinfo_props;
420 apr_hash_index_t *hi;
422 SVN_ERR(svn_wc__prop_retrieve_recursive(&mergeinfo_props,
423 ctx->wc_ctx, local_abspath,
425 scratch_pool, scratch_pool));
427 /* Convert *mergeinfo_props into a proper svn_mergeinfo_catalog_t */
428 for (hi = apr_hash_first(scratch_pool, mergeinfo_props);
430 hi = apr_hash_next(hi))
432 const char *node_abspath = svn__apr_hash_index_key(hi);
433 svn_string_t *propval = svn__apr_hash_index_val(hi);
434 svn_mergeinfo_t subtree_mergeinfo;
435 const char *repos_relpath;
437 if (strcmp(node_abspath, local_abspath) == 0)
438 continue; /* Already parsed in svn_client__get_wc_mergeinfo */
440 SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, NULL, NULL,
441 ctx->wc_ctx, node_abspath,
442 result_pool, scratch_pool));
444 SVN_ERR(svn_mergeinfo_parse(&subtree_mergeinfo, propval->data,
447 /* If the target had no explicit/inherited mergeinfo and this is the
448 first subtree with mergeinfo found, then the catalog will still
450 if (*mergeinfo_cat == NULL)
451 *mergeinfo_cat = apr_hash_make(result_pool);
453 svn_hash_sets(*mergeinfo_cat, repos_relpath, subtree_mergeinfo);
461 svn_client__get_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo,
462 svn_ra_session_t *ra_session,
465 svn_mergeinfo_inheritance_t inherit,
466 svn_boolean_t squelch_incapable,
469 svn_mergeinfo_catalog_t tgt_mergeinfo_cat;
471 *target_mergeinfo = NULL;
473 SVN_ERR(svn_client__get_repos_mergeinfo_catalog(&tgt_mergeinfo_cat,
476 squelch_incapable, FALSE,
479 if (tgt_mergeinfo_cat && apr_hash_count(tgt_mergeinfo_cat))
481 /* We asked only for the REL_PATH's mergeinfo, not any of its
482 descendants. So if there is anything in the catalog it is the
483 mergeinfo for REL_PATH. */
485 svn__apr_hash_index_val(apr_hash_first(pool, tgt_mergeinfo_cat));
493 svn_client__get_repos_mergeinfo_catalog(svn_mergeinfo_catalog_t *mergeinfo_cat,
494 svn_ra_session_t *ra_session,
497 svn_mergeinfo_inheritance_t inherit,
498 svn_boolean_t squelch_incapable,
499 svn_boolean_t include_descendants,
500 apr_pool_t *result_pool,
501 apr_pool_t *scratch_pool)
504 svn_mergeinfo_catalog_t repos_mergeinfo_cat;
505 apr_array_header_t *rel_paths = apr_array_make(scratch_pool, 1,
506 sizeof(const char *));
507 const char *old_session_url;
509 APR_ARRAY_PUSH(rel_paths, const char *) = "";
511 /* Fetch the mergeinfo. */
512 SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url,
513 ra_session, url, scratch_pool));
514 err = svn_ra_get_mergeinfo(ra_session, &repos_mergeinfo_cat, rel_paths,
515 rev, inherit, include_descendants, result_pool);
516 err = svn_error_compose_create(
517 err, svn_ra_reparent(ra_session, old_session_url, scratch_pool));
520 if (squelch_incapable && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
522 svn_error_clear(err);
523 *mergeinfo_cat = NULL;
527 return svn_error_trace(err);
530 if (repos_mergeinfo_cat == NULL)
532 *mergeinfo_cat = NULL;
536 const char *session_relpath;
538 SVN_ERR(svn_ra_get_path_relative_to_root(ra_session, &session_relpath,
541 if (session_relpath[0] == '\0')
542 *mergeinfo_cat = repos_mergeinfo_cat;
544 SVN_ERR(svn_mergeinfo__add_prefix_to_catalog(mergeinfo_cat,
555 svn_client__get_wc_or_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo,
556 svn_boolean_t *inherited,
557 svn_boolean_t *from_repos,
558 svn_boolean_t repos_only,
559 svn_mergeinfo_inheritance_t inherit,
560 svn_ra_session_t *ra_session,
561 const char *target_wcpath,
562 svn_client_ctx_t *ctx,
565 svn_mergeinfo_catalog_t tgt_mergeinfo_cat;
567 *target_mergeinfo = NULL;
569 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo_catalog(&tgt_mergeinfo_cat,
570 inherited, from_repos,
577 if (tgt_mergeinfo_cat && apr_hash_count(tgt_mergeinfo_cat))
579 /* We asked only for the TARGET_WCPATH's mergeinfo, not any of its
580 descendants. It this mergeinfo is in the catalog, it's keyed
581 on TARGET_WCPATH's root-relative path. We could dig that up
582 so we can peek into our catalog, but it ought to be the only
583 thing in the catalog, so we'll just fetch the first hash item. */
585 svn__apr_hash_index_val(apr_hash_first(pool, tgt_mergeinfo_cat));
593 svn_client__get_wc_or_repos_mergeinfo_catalog(
594 svn_mergeinfo_catalog_t *target_mergeinfo_catalog,
595 svn_boolean_t *inherited_p,
596 svn_boolean_t *from_repos,
597 svn_boolean_t include_descendants,
598 svn_boolean_t repos_only,
599 svn_boolean_t ignore_invalid_mergeinfo,
600 svn_mergeinfo_inheritance_t inherit,
601 svn_ra_session_t *ra_session,
602 const char *target_wcpath,
603 svn_client_ctx_t *ctx,
604 apr_pool_t *result_pool,
605 apr_pool_t *scratch_pool)
608 svn_revnum_t target_rev;
609 const char *local_abspath;
610 const char *repos_root;
611 const char *repos_relpath;
612 svn_mergeinfo_catalog_t target_mergeinfo_cat_wc = NULL;
613 svn_mergeinfo_catalog_t target_mergeinfo_cat_repos = NULL;
615 SVN_ERR(svn_dirent_get_absolute(&local_abspath, target_wcpath,
621 /* We may get an entry with abbreviated information from TARGET_WCPATH's
622 parent if TARGET_WCPATH is missing. These limited entries do not have
623 a URL and without that we cannot get accurate mergeinfo for
625 SVN_ERR(svn_wc__node_get_origin(NULL, &target_rev, &repos_relpath,
626 &repos_root, NULL, NULL,
627 ctx->wc_ctx, local_abspath, FALSE,
628 scratch_pool, scratch_pool));
631 url = svn_path_url_add_component2(repos_root, repos_relpath, scratch_pool);
637 svn_boolean_t inherited;
638 SVN_ERR(svn_client__get_wc_mergeinfo_catalog(&target_mergeinfo_cat_wc,
644 ignore_invalid_mergeinfo,
649 *inherited_p = inherited;
651 /* If we want LOCAL_ABSPATH's inherited mergeinfo, were we able to
652 get it from the working copy? If not, then we must ask the
655 || (inherit == svn_mergeinfo_explicit)
657 && target_mergeinfo_cat_wc
658 && svn_hash_gets(target_mergeinfo_cat_wc, repos_relpath))))
661 /* We already have any subtree mergeinfo from the working copy, no
662 need to ask the server for it again. */
663 include_descendants = FALSE;
669 /* No need to check the repos if this is a local addition. */
672 apr_hash_t *original_props;
674 /* Check to see if we have local modifications which removed all of
675 TARGET_WCPATH's pristine mergeinfo. If that is the case then
676 TARGET_WCPATH effectively has no mergeinfo. */
677 SVN_ERR(svn_wc_get_pristine_props(&original_props,
678 ctx->wc_ctx, local_abspath,
679 result_pool, scratch_pool));
680 if (!svn_hash_gets(original_props, SVN_PROP_MERGEINFO))
682 apr_pool_t *sesspool = NULL;
686 sesspool = svn_pool_create(scratch_pool);
687 SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL,
689 sesspool, sesspool));
692 SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
693 &target_mergeinfo_cat_repos, ra_session,
694 url, target_rev, inherit,
695 TRUE, include_descendants,
696 result_pool, scratch_pool));
698 if (target_mergeinfo_cat_repos
699 && svn_hash_gets(target_mergeinfo_cat_repos, repos_relpath))
707 /* If we created an RA_SESSION above, destroy it.
708 Otherwise, if reparented an existing session, point
709 it back where it was when we were called. */
712 svn_pool_destroy(sesspool);
718 /* Combine the mergeinfo from the working copy and repository as needed. */
719 if (target_mergeinfo_cat_wc)
721 *target_mergeinfo_catalog = target_mergeinfo_cat_wc;
722 if (target_mergeinfo_cat_repos)
723 SVN_ERR(svn_mergeinfo_catalog_merge(*target_mergeinfo_catalog,
724 target_mergeinfo_cat_repos,
725 result_pool, scratch_pool));
727 else if (target_mergeinfo_cat_repos)
729 *target_mergeinfo_catalog = target_mergeinfo_cat_repos;
733 *target_mergeinfo_catalog = NULL;
741 svn_client__get_history_as_mergeinfo(svn_mergeinfo_t *mergeinfo_p,
742 svn_boolean_t *has_rev_zero_history,
743 const svn_client__pathrev_t *pathrev,
744 svn_revnum_t range_youngest,
745 svn_revnum_t range_oldest,
746 svn_ra_session_t *ra_session,
747 svn_client_ctx_t *ctx,
750 apr_array_header_t *segments;
752 /* Fetch the location segments for our URL@PEG_REVNUM. */
753 if (! SVN_IS_VALID_REVNUM(range_youngest))
754 range_youngest = pathrev->rev;
755 if (! SVN_IS_VALID_REVNUM(range_oldest))
758 SVN_ERR(svn_client__repos_location_segments(&segments, ra_session,
759 pathrev->url, pathrev->rev,
760 range_youngest, range_oldest,
763 if (has_rev_zero_history)
765 *has_rev_zero_history = FALSE;
768 svn_location_segment_t *oldest_segment =
769 APR_ARRAY_IDX(segments, 0, svn_location_segment_t *);
770 if (oldest_segment->range_start == 0)
771 *has_rev_zero_history = TRUE;
775 SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(mergeinfo_p, segments, pool));
781 /*-----------------------------------------------------------------------*/
783 /*** Eliding mergeinfo. ***/
785 /* Given the mergeinfo (CHILD_MERGEINFO) for a path, and the
786 mergeinfo of its nearest ancestor with mergeinfo (PARENT_MERGEINFO), compare
787 CHILD_MERGEINFO to PARENT_MERGEINFO to see if the former elides to
788 the latter, following the elision rules described in
789 svn_client__elide_mergeinfo()'s docstring. Set *ELIDES to whether
790 or not CHILD_MERGEINFO is redundant.
792 Note: This function assumes that PARENT_MERGEINFO is definitive;
793 i.e. if it is NULL then the caller not only walked the entire WC
794 looking for inherited mergeinfo, but queried the repository if none
795 was found in the WC. This is rather important since this function
796 says empty mergeinfo should be elided if PARENT_MERGEINFO is NULL,
797 and we don't want to do that unless we are *certain* that the empty
798 mergeinfo on PATH isn't overriding anything.
800 If PATH_SUFFIX and PARENT_MERGEINFO are not NULL append PATH_SUFFIX
801 to each path in PARENT_MERGEINFO before performing the comparison. */
803 should_elide_mergeinfo(svn_boolean_t *elides,
804 svn_mergeinfo_t parent_mergeinfo,
805 svn_mergeinfo_t child_mergeinfo,
806 const char *path_suffix,
807 apr_pool_t *scratch_pool)
809 /* Easy out: No child mergeinfo to elide. */
810 if (child_mergeinfo == NULL)
814 else if (apr_hash_count(child_mergeinfo) == 0)
816 /* Empty mergeinfo elides to empty mergeinfo or to "nothing",
817 i.e. it isn't overriding any parent. Otherwise it doesn't
819 *elides = (!parent_mergeinfo || apr_hash_count(parent_mergeinfo) == 0);
821 else if (!parent_mergeinfo || apr_hash_count(parent_mergeinfo) == 0)
823 /* Non-empty mergeinfo never elides to empty mergeinfo
829 /* Both CHILD_MERGEINFO and PARENT_MERGEINFO are non-NULL and
831 svn_mergeinfo_t path_tweaked_parent_mergeinfo;
833 /* If we need to adjust the paths in PARENT_MERGEINFO do it now. */
835 SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
836 &path_tweaked_parent_mergeinfo, parent_mergeinfo,
837 path_suffix, scratch_pool, scratch_pool));
839 path_tweaked_parent_mergeinfo = parent_mergeinfo;
841 SVN_ERR(svn_mergeinfo__equals(elides,
842 path_tweaked_parent_mergeinfo,
843 child_mergeinfo, TRUE, scratch_pool));
849 /* Helper for svn_client__elide_mergeinfo().
851 Given a working copy LOCAL_ABSPATH, its mergeinfo hash CHILD_MERGEINFO, and
852 the mergeinfo of LOCAL_ABSPATH's nearest ancestor PARENT_MERGEINFO, use
853 should_elide_mergeinfo() to decide whether or not CHILD_MERGEINFO elides to
854 PARENT_MERGEINFO; PATH_SUFFIX means the same as in that function.
856 If elision does occur, then remove the mergeinfo for LOCAL_ABSPATH.
858 If CHILD_MERGEINFO is NULL, do nothing.
860 Use SCRATCH_POOL for temporary allocations.
863 elide_mergeinfo(svn_mergeinfo_t parent_mergeinfo,
864 svn_mergeinfo_t child_mergeinfo,
865 const char *local_abspath,
866 svn_client_ctx_t *ctx,
867 apr_pool_t *scratch_pool)
869 svn_boolean_t elides;
871 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
873 SVN_ERR(should_elide_mergeinfo(&elides,
874 parent_mergeinfo, child_mergeinfo, NULL,
879 SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath, SVN_PROP_MERGEINFO,
880 NULL, svn_depth_empty, TRUE, NULL,
881 NULL, NULL /* cancellation */,
882 NULL, NULL /* notification */,
885 if (ctx->notify_func2)
887 svn_wc_notify_t *notify;
889 notify = svn_wc_create_notify(local_abspath,
890 svn_wc_notify_merge_elide_info,
892 ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
894 notify = svn_wc_create_notify(local_abspath,
895 svn_wc_notify_update_update,
897 notify->prop_state = svn_wc_notify_state_changed;
899 ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
908 svn_client__elide_mergeinfo(const char *target_abspath,
909 const char *wc_elision_limit_abspath,
910 svn_client_ctx_t *ctx,
913 const char *limit_abspath = wc_elision_limit_abspath;
915 SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
916 SVN_ERR_ASSERT(!wc_elision_limit_abspath || svn_dirent_is_absolute(wc_elision_limit_abspath));
918 /* Check for first easy out: We are already at the limit path. */
920 || strcmp(target_abspath, limit_abspath) != 0)
922 svn_mergeinfo_t target_mergeinfo;
923 svn_mergeinfo_t mergeinfo = NULL;
924 svn_boolean_t inherited;
925 const char *walk_path;
928 /* Get the TARGET_WCPATH's explicit mergeinfo. */
929 err = svn_client__get_wc_mergeinfo(&target_mergeinfo, &inherited,
930 svn_mergeinfo_inherited,
937 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
939 /* Issue #3896: If we attempt elision because invalid
940 mergeinfo is present on TARGET_WCPATH, then don't let
941 the merge fail, just skip the elision attempt. */
942 svn_error_clear(err);
947 return svn_error_trace(err);
951 /* If TARGET_WCPATH has no explicit mergeinfo, there's nothing to
952 elide, we're done. */
953 if (inherited || target_mergeinfo == NULL)
956 /* Get TARGET_WCPATH's inherited mergeinfo from the WC. */
957 err = svn_client__get_wc_mergeinfo(&mergeinfo, NULL,
958 svn_mergeinfo_nearest_ancestor,
961 &walk_path, FALSE, ctx, pool, pool);
964 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
966 /* Issue #3896 again, but invalid mergeinfo is inherited. */
967 svn_error_clear(err);
972 return svn_error_trace(err);
976 /* If TARGET_WCPATH inherited no mergeinfo from the WC and we are
977 not limiting our search to the working copy then check if it
978 inherits any from the repos. */
979 if (!mergeinfo && !wc_elision_limit_abspath)
981 err = svn_client__get_wc_or_repos_mergeinfo(
982 &mergeinfo, NULL, NULL, TRUE,
983 svn_mergeinfo_nearest_ancestor,
984 NULL, target_abspath, ctx, pool);
987 if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
989 /* Issue #3896 again, but invalid mergeinfo is inherited
990 from the repository. */
991 svn_error_clear(err);
996 return svn_error_trace(err);
1001 /* If there is nowhere to elide TARGET_WCPATH's mergeinfo to and
1002 the elision is limited, then we are done.*/
1003 if (!mergeinfo && wc_elision_limit_abspath)
1004 return SVN_NO_ERROR;
1006 SVN_ERR(elide_mergeinfo(mergeinfo, target_mergeinfo, target_abspath,
1009 return SVN_NO_ERROR;
1013 /* Set *MERGEINFO_CATALOG to the explicit or inherited mergeinfo for
1014 PATH_OR_URL@PEG_REVISION. If INCLUDE_DESCENDANTS is true, also
1015 store in *MERGEINFO_CATALOG the explicit mergeinfo on any subtrees
1016 under PATH_OR_URL. Key all mergeinfo in *MERGEINFO_CATALOG on
1017 repository relpaths.
1019 If no mergeinfo is found then set *MERGEINFO_CATALOG to NULL.
1021 Set *REPOS_ROOT to the root URL of the repository associated with
1024 Allocate *MERGEINFO_CATALOG and all its contents in RESULT_POOL. Use
1025 SCRATCH_POOL for all temporary allocations.
1027 Return SVN_ERR_UNSUPPORTED_FEATURE if the server does not support
1029 static svn_error_t *
1030 get_mergeinfo(svn_mergeinfo_catalog_t *mergeinfo_catalog,
1031 const char **repos_root,
1032 const char *path_or_url,
1033 const svn_opt_revision_t *peg_revision,
1034 svn_boolean_t include_descendants,
1035 svn_boolean_t ignore_invalid_mergeinfo,
1036 svn_client_ctx_t *ctx,
1037 apr_pool_t *result_pool,
1038 apr_pool_t *scratch_pool)
1040 svn_ra_session_t *ra_session;
1041 const char *local_abspath;
1042 svn_boolean_t use_url = svn_path_is_url(path_or_url);
1043 svn_client__pathrev_t *peg_loc;
1045 SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &peg_loc,
1046 path_or_url, NULL, peg_revision,
1047 peg_revision, ctx, scratch_pool));
1049 /* If PATH_OR_URL is as working copy path determine if we will need to
1050 contact the repository for the requested PEG_REVISION. */
1053 svn_client__pathrev_t *origin;
1054 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
1057 SVN_ERR(svn_client__wc_node_get_origin(&origin, local_abspath, ctx,
1058 scratch_pool, scratch_pool));
1060 || strcmp(origin->url, peg_loc->url) != 0
1061 || peg_loc->rev != origin->rev)
1063 use_url = TRUE; /* Don't rely on local mergeinfo */
1067 /* Check server Merge Tracking capability. */
1068 SVN_ERR(svn_ra__assert_mergeinfo_capable_server(ra_session, path_or_url,
1071 SVN_ERR(svn_ra_get_repos_root2(ra_session, repos_root, result_pool));
1075 SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
1076 mergeinfo_catalog, ra_session, peg_loc->url, peg_loc->rev,
1077 svn_mergeinfo_inherited, FALSE, include_descendants,
1078 result_pool, scratch_pool));
1080 else /* ! svn_path_is_url() */
1082 SVN_ERR(svn_client__get_wc_or_repos_mergeinfo_catalog(
1083 mergeinfo_catalog, NULL, NULL, include_descendants, FALSE,
1084 ignore_invalid_mergeinfo, svn_mergeinfo_inherited,
1085 ra_session, path_or_url, ctx,
1086 result_pool, scratch_pool));
1089 return SVN_NO_ERROR;
1092 /*** In-memory mergeinfo elision ***/
1094 svn_client__elide_mergeinfo_catalog(svn_mergeinfo_catalog_t mergeinfo_catalog,
1095 apr_pool_t *scratch_pool)
1097 apr_array_header_t *sorted_hash;
1098 apr_array_header_t *elidable_paths = apr_array_make(scratch_pool, 1,
1099 sizeof(const char *));
1100 apr_array_header_t *dir_stack = apr_array_make(scratch_pool, 1,
1101 sizeof(const char *));
1102 apr_pool_t *iterpool;
1105 /* Here's the general algorithm:
1106 Walk through the paths sorted in tree order. For each path, pop
1107 the dir_stack until it is either empty or the top item contains a parent
1108 of the current path. Check to see if that mergeinfo is then elidable,
1109 and build the list of elidable mergeinfo based upon that determination.
1110 Finally, push the path of interest onto the stack, and continue. */
1111 sorted_hash = svn_sort__hash(mergeinfo_catalog,
1112 svn_sort_compare_items_as_paths,
1114 iterpool = svn_pool_create(scratch_pool);
1115 for (i = 0; i < sorted_hash->nelts; i++)
1117 svn_sort__item_t *item = &APR_ARRAY_IDX(sorted_hash, i,
1119 const char *path = item->key;
1121 if (dir_stack->nelts > 0)
1124 const char *path_suffix;
1125 svn_boolean_t elides = FALSE;
1127 svn_pool_clear(iterpool);
1129 /* Pop off any paths which are not ancestors of PATH. */
1132 top = APR_ARRAY_IDX(dir_stack, dir_stack->nelts - 1,
1134 path_suffix = svn_dirent_is_child(top, path, NULL);
1137 apr_array_pop(dir_stack);
1139 while (dir_stack->nelts > 0 && !path_suffix);
1141 /* If we have a path suffix, it means we haven't popped the stack
1145 SVN_ERR(should_elide_mergeinfo(&elides,
1146 svn_hash_gets(mergeinfo_catalog, top),
1147 svn_hash_gets(mergeinfo_catalog, path),
1152 APR_ARRAY_PUSH(elidable_paths, const char *) = path;
1156 APR_ARRAY_PUSH(dir_stack, const char *) = path;
1158 svn_pool_destroy(iterpool);
1160 /* Now remove the elidable paths from the catalog. */
1161 for (i = 0; i < elidable_paths->nelts; i++)
1163 const char *path = APR_ARRAY_IDX(elidable_paths, i, const char *);
1164 svn_hash_sets(mergeinfo_catalog, path, NULL);
1167 return SVN_NO_ERROR;
1171 /* Helper for filter_log_entry_with_rangelist().
1173 DEPTH_FIRST_CATALOG_INDEX is an array of svn_sort__item_t's. The keys are
1174 repository-absolute const char *paths, the values are svn_mergeinfo_t for
1177 Return a pointer to the mergeinfo value of the nearest path-wise ancestor
1178 of FSPATH in DEPTH_FIRST_CATALOG_INDEX. A path is considered its
1179 own ancestor, so if a key exactly matches FSPATH, return that
1180 key's mergeinfo and set *ANCESTOR_IS_SELF to true (set it to false in all
1183 If DEPTH_FIRST_CATALOG_INDEX is NULL, empty, or no ancestor is found, then
1185 static svn_mergeinfo_t
1186 find_nearest_ancestor(const apr_array_header_t *depth_first_catalog_index,
1187 svn_boolean_t *ancestor_is_self,
1190 int ancestor_index = -1;
1192 *ancestor_is_self = FALSE;
1194 if (depth_first_catalog_index)
1198 for (i = 0; i < depth_first_catalog_index->nelts; i++)
1200 svn_sort__item_t item = APR_ARRAY_IDX(depth_first_catalog_index, i,
1202 if (svn_fspath__skip_ancestor(item.key, fspath))
1206 /* There's no nearer ancestor than FSPATH itself. */
1207 if (strcmp(item.key, fspath) == 0)
1209 *ancestor_is_self = TRUE;
1217 if (ancestor_index == -1)
1220 return (APR_ARRAY_IDX(depth_first_catalog_index,
1222 svn_sort__item_t)).value;
1225 /* Baton for use with the filter_log_entry_with_rangelist()
1226 svn_log_entry_receiver_t callback. */
1227 struct filter_log_entry_baton_t
1229 /* Is TRUE if RANGELIST describes potentially merged revisions, is FALSE
1230 if RANGELIST describes potentially eligible revisions. */
1231 svn_boolean_t filtering_merged;
1233 /* Unsorted array of repository relative paths representing the merge
1234 sources. There will be more than one source */
1235 const apr_array_header_t *merge_source_fspaths;
1237 /* The repository-absolute path we are calling svn_client_log5() on. */
1238 const char *target_fspath;
1240 /* Mergeinfo catalog for the tree rooted at TARGET_FSPATH.
1241 The path keys must be repository-absolute. */
1242 svn_mergeinfo_catalog_t target_mergeinfo_catalog;
1244 /* Depth first sorted array of svn_sort__item_t's for
1245 TARGET_MERGEINFO_CATALOG. */
1246 apr_array_header_t *depth_first_catalog_index;
1248 /* A rangelist describing all the revisions potentially merged or
1249 potentially eligible for merging (see FILTERING_MERGED) based on
1250 the target's explicit or inherited mergeinfo. */
1251 const svn_rangelist_t *rangelist;
1253 /* The wrapped svn_log_entry_receiver_t callback and baton which
1254 filter_log_entry_with_rangelist() is acting as a filter for. */
1255 svn_log_entry_receiver_t log_receiver;
1256 void *log_receiver_baton;
1258 svn_client_ctx_t *ctx;
1261 /* Implements the svn_log_entry_receiver_t interface. BATON is a
1262 `struct filter_log_entry_baton_t *'.
1264 Call the wrapped log receiver BATON->log_receiver (with
1265 BATON->log_receiver_baton) if:
1267 BATON->FILTERING_MERGED is FALSE and the changes represented by LOG_ENTRY
1268 have been fully merged from BATON->merge_source_fspaths to the WC target
1269 based on the mergeinfo for the WC contained in BATON->TARGET_MERGEINFO_CATALOG.
1273 BATON->FILTERING_MERGED is TRUE and the changes represented by LOG_ENTRY
1274 have not been merged, or only partially merged, from
1275 BATON->merge_source_fspaths to the WC target based on the mergeinfo for the
1276 WC contained in BATON->TARGET_MERGEINFO_CATALOG. */
1277 static svn_error_t *
1278 filter_log_entry_with_rangelist(void *baton,
1279 svn_log_entry_t *log_entry,
1282 struct filter_log_entry_baton_t *fleb = baton;
1283 svn_rangelist_t *intersection, *this_rangelist;
1285 if (fleb->ctx->cancel_func)
1286 SVN_ERR(fleb->ctx->cancel_func(fleb->ctx->cancel_baton));
1288 /* Ignore r0 because there can be no "change 0" in a merge range. */
1289 if (log_entry->revision == 0)
1290 return SVN_NO_ERROR;
1292 this_rangelist = svn_rangelist__initialize(log_entry->revision - 1,
1293 log_entry->revision,
1296 /* Don't consider inheritance yet, see if LOG_ENTRY->REVISION is
1297 fully or partially represented in BATON->RANGELIST. */
1298 SVN_ERR(svn_rangelist_intersect(&intersection, fleb->rangelist,
1299 this_rangelist, FALSE, pool));
1300 if (! (intersection && intersection->nelts))
1301 return SVN_NO_ERROR;
1303 SVN_ERR_ASSERT(intersection->nelts == 1);
1305 /* Ok, we know LOG_ENTRY->REVISION is represented in BATON->RANGELIST,
1306 but is it only partially represented, i.e. is the corresponding range in
1307 BATON->RANGELIST non-inheritable? Ask for the same intersection as
1308 above but consider inheritance this time, if the intersection is empty
1309 we know the range in BATON->RANGELIST is non-inheritable. */
1310 SVN_ERR(svn_rangelist_intersect(&intersection, fleb->rangelist,
1311 this_rangelist, TRUE, pool));
1312 log_entry->non_inheritable = !intersection->nelts;
1314 /* If the paths changed by LOG_ENTRY->REVISION are provided we can determine
1315 if LOG_ENTRY->REVISION, while only partially represented in
1316 BATON->RANGELIST, is in fact completely applied to all affected paths.
1317 ### And ... what if it is, or if it isn't? What do we do with the answer?
1318 And how do we cope if the changed paths are not provided? */
1319 if ((log_entry->non_inheritable || !fleb->filtering_merged)
1320 && log_entry->changed_paths2)
1322 apr_hash_index_t *hi;
1323 svn_boolean_t all_subtrees_have_this_rev = TRUE;
1324 svn_rangelist_t *this_rev_rangelist =
1325 svn_rangelist__initialize(log_entry->revision - 1,
1326 log_entry->revision, TRUE, pool);
1327 apr_pool_t *iterpool = svn_pool_create(pool);
1329 for (hi = apr_hash_first(pool, log_entry->changed_paths2);
1331 hi = apr_hash_next(hi))
1334 const char *path = svn__apr_hash_index_key(hi);
1335 svn_log_changed_path2_t *change = svn__apr_hash_index_val(hi);
1336 const char *target_fspath_affected;
1337 svn_mergeinfo_t nearest_ancestor_mergeinfo;
1338 svn_boolean_t found_this_revision = FALSE;
1339 const char *merge_source_rel_target;
1340 const char *merge_source_fspath;
1341 svn_boolean_t ancestor_is_self;
1343 svn_pool_clear(iterpool);
1345 /* Check that PATH is a subtree of at least one of the
1346 merge sources. If not then ignore this path. */
1347 for (i = 0; i < fleb->merge_source_fspaths->nelts; i++)
1349 merge_source_fspath = APR_ARRAY_IDX(fleb->merge_source_fspaths,
1352 merge_source_rel_target
1353 = svn_fspath__skip_ancestor(merge_source_fspath, path);
1354 if (merge_source_rel_target)
1356 /* If MERGE_SOURCE was itself deleted, replaced, or added
1357 in LOG_ENTRY->REVISION then ignore this PATH since you
1358 can't merge a addition or deletion of yourself. */
1359 if (merge_source_rel_target[0] == '\0'
1360 && (change->action != 'M'))
1361 i = fleb->merge_source_fspaths->nelts;
1365 /* If we examined every merge source path and PATH is a child of
1366 none of them then we can ignore this PATH. */
1367 if (i == fleb->merge_source_fspaths->nelts)
1370 /* Calculate the target path which PATH would affect if merged. */
1371 target_fspath_affected = svn_fspath__join(fleb->target_fspath,
1372 merge_source_rel_target,
1375 nearest_ancestor_mergeinfo =
1376 find_nearest_ancestor(fleb->depth_first_catalog_index,
1378 target_fspath_affected);
1380 /* Issue #3791: A path should never have explicit mergeinfo
1381 describing its own addition (that's self-referential). Nor will
1382 it have explicit mergeinfo describing its own deletion (we
1383 obviously can't add new mergeinfo to a path we are deleting).
1385 This lack of explicit mergeinfo should not cause such revisions
1386 to show up as eligible however. If PATH was deleted, replaced,
1387 or added in LOG_ENTRY->REVISION, but the corresponding
1388 TARGET_PATH_AFFECTED already exists and has explicit mergeinfo
1389 describing merges from PATH *after* LOG_ENTRY->REVISION, then
1390 ignore this PATH. If it was deleted in LOG_ENTRY->REVISION it's
1391 obviously back. If it was added or replaced it's still around
1392 possibly it was replaced one or more times, but it's back now.
1393 Regardless, LOG_ENTRY->REVISION is *not* an eligible revision! */
1394 if (ancestor_is_self /* Explicit mergeinfo on TARGET_PATH_AFFECTED */
1395 && (change->action != 'M'))
1397 svn_rangelist_t *rangelist =
1398 svn_hash_gets(nearest_ancestor_mergeinfo, path);
1399 svn_merge_range_t *youngest_range = APR_ARRAY_IDX(
1400 rangelist, rangelist->nelts - 1, svn_merge_range_t *);
1403 && (youngest_range->end > log_entry->revision))
1407 if (nearest_ancestor_mergeinfo)
1409 apr_hash_index_t *hi2;
1411 for (hi2 = apr_hash_first(iterpool, nearest_ancestor_mergeinfo);
1413 hi2 = apr_hash_next(hi2))
1415 const char *mergeinfo_path = svn__apr_hash_index_key(hi2);
1416 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi2);
1418 /* Does the mergeinfo for PATH reflect if
1419 LOG_ENTRY->REVISION was previously merged
1420 from MERGE_SOURCE_FSPATH? */
1421 if (svn_fspath__skip_ancestor(merge_source_fspath,
1424 /* Something was merged from MERGE_SOURCE_FSPATH, does
1425 it include LOG_ENTRY->REVISION? */
1426 SVN_ERR(svn_rangelist_intersect(&intersection,
1431 if (intersection->nelts)
1433 if (ancestor_is_self)
1435 /* TARGET_PATH_AFFECTED has explicit mergeinfo,
1436 so we don't need to worry if that mergeinfo
1437 is inheritable or not. */
1438 found_this_revision = TRUE;
1443 /* TARGET_PATH_AFFECTED inherited its mergeinfo,
1444 so we have to ignore non-inheritable
1446 SVN_ERR(svn_rangelist_intersect(
1451 if (intersection->nelts)
1453 found_this_revision = TRUE;
1462 if (!found_this_revision)
1464 /* As soon as any PATH is found that is not fully merged for
1465 LOG_ENTRY->REVISION then we can stop. */
1466 all_subtrees_have_this_rev = FALSE;
1471 svn_pool_destroy(iterpool);
1473 if (all_subtrees_have_this_rev)
1475 if (fleb->filtering_merged)
1476 log_entry->non_inheritable = FALSE;
1478 return SVN_NO_ERROR;
1482 /* Call the wrapped log receiver which this function is filtering for. */
1483 return fleb->log_receiver(fleb->log_receiver_baton, log_entry, pool);
1486 static svn_error_t *
1487 logs_for_mergeinfo_rangelist(const char *source_url,
1488 const apr_array_header_t *merge_source_fspaths,
1489 svn_boolean_t filtering_merged,
1490 const svn_rangelist_t *rangelist,
1491 svn_boolean_t oldest_revs_first,
1492 svn_mergeinfo_catalog_t target_mergeinfo_catalog,
1493 const char *target_fspath,
1494 svn_boolean_t discover_changed_paths,
1495 const apr_array_header_t *revprops,
1496 svn_log_entry_receiver_t log_receiver,
1497 void *log_receiver_baton,
1498 svn_client_ctx_t *ctx,
1499 apr_pool_t *scratch_pool)
1501 apr_array_header_t *target;
1502 svn_merge_range_t *oldest_range, *youngest_range;
1503 apr_array_header_t *revision_ranges;
1504 svn_opt_revision_t oldest_rev, youngest_rev;
1505 struct filter_log_entry_baton_t fleb;
1507 if (! rangelist->nelts)
1508 return SVN_NO_ERROR;
1510 /* Sort the rangelist. */
1511 qsort(rangelist->elts, rangelist->nelts,
1512 rangelist->elt_size, svn_sort_compare_ranges);
1514 /* Build a single-member log target list using SOURCE_URL. */
1515 target = apr_array_make(scratch_pool, 1, sizeof(const char *));
1516 APR_ARRAY_PUSH(target, const char *) = source_url;
1518 /* Calculate and construct the bounds of our log request. */
1519 youngest_range = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
1520 svn_merge_range_t *);
1521 youngest_rev.kind = svn_opt_revision_number;
1522 youngest_rev.value.number = youngest_range->end;
1523 oldest_range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
1524 oldest_rev.kind = svn_opt_revision_number;
1525 oldest_rev.value.number = oldest_range->start;
1527 if (! target_mergeinfo_catalog)
1528 target_mergeinfo_catalog = apr_hash_make(scratch_pool);
1530 /* FILTER_LOG_ENTRY_BATON_T->TARGET_MERGEINFO_CATALOG's keys are required
1531 to be repository-absolute. */
1532 SVN_ERR(svn_mergeinfo__add_prefix_to_catalog(&target_mergeinfo_catalog,
1533 target_mergeinfo_catalog, "/",
1534 scratch_pool, scratch_pool));
1536 /* Build the log filtering callback baton. */
1537 fleb.filtering_merged = filtering_merged;
1538 fleb.merge_source_fspaths = merge_source_fspaths;
1539 fleb.target_mergeinfo_catalog = target_mergeinfo_catalog;
1540 fleb.depth_first_catalog_index =
1541 svn_sort__hash(target_mergeinfo_catalog,
1542 svn_sort_compare_items_as_paths,
1544 fleb.target_fspath = target_fspath;
1545 fleb.rangelist = rangelist;
1546 fleb.log_receiver = log_receiver;
1547 fleb.log_receiver_baton = log_receiver_baton;
1550 /* Drive the log. */
1551 revision_ranges = apr_array_make(scratch_pool, 1,
1552 sizeof(svn_opt_revision_range_t *));
1553 if (oldest_revs_first)
1554 APR_ARRAY_PUSH(revision_ranges, svn_opt_revision_range_t *)
1555 = svn_opt__revision_range_create(&oldest_rev, &youngest_rev, scratch_pool);
1557 APR_ARRAY_PUSH(revision_ranges, svn_opt_revision_range_t *)
1558 = svn_opt__revision_range_create(&youngest_rev, &oldest_rev, scratch_pool);
1559 SVN_ERR(svn_client_log5(target, &youngest_rev, revision_ranges,
1560 0, discover_changed_paths, FALSE, FALSE, revprops,
1561 filter_log_entry_with_rangelist, &fleb, ctx,
1564 /* Check for cancellation. */
1565 if (ctx->cancel_func)
1566 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
1568 return SVN_NO_ERROR;
1571 /* Set *OUT_MERGEINFO to a shallow copy of MERGEINFO with each source path
1572 converted to a (URI-encoded) URL based on REPOS_ROOT_URL. *OUT_MERGEINFO
1573 is declared as 'apr_hash_t *' because its key do not obey the rules of
1576 Allocate *OUT_MERGEINFO and the new keys in RESULT_POOL. Use
1577 SCRATCH_POOL for any temporary allocations. */
1578 static svn_error_t *
1579 mergeinfo_relpaths_to_urls(apr_hash_t **out_mergeinfo,
1580 svn_mergeinfo_t mergeinfo,
1581 const char *repos_root_url,
1582 apr_pool_t *result_pool,
1583 apr_pool_t *scratch_pool)
1585 *out_mergeinfo = NULL;
1588 apr_hash_index_t *hi;
1589 apr_hash_t *full_path_mergeinfo = apr_hash_make(result_pool);
1591 for (hi = apr_hash_first(scratch_pool, mergeinfo);
1592 hi; hi = apr_hash_next(hi))
1594 const char *key = svn__apr_hash_index_key(hi);
1595 void *val = svn__apr_hash_index_val(hi);
1597 svn_hash_sets(full_path_mergeinfo,
1598 svn_path_url_add_component2(repos_root_url, key + 1,
1602 *out_mergeinfo = full_path_mergeinfo;
1605 return SVN_NO_ERROR;
1609 /*** Public APIs ***/
1612 svn_client_mergeinfo_get_merged(apr_hash_t **mergeinfo_p,
1613 const char *path_or_url,
1614 const svn_opt_revision_t *peg_revision,
1615 svn_client_ctx_t *ctx,
1618 const char *repos_root;
1619 svn_mergeinfo_catalog_t mergeinfo_cat;
1620 svn_mergeinfo_t mergeinfo;
1622 SVN_ERR(get_mergeinfo(&mergeinfo_cat, &repos_root, path_or_url,
1623 peg_revision, FALSE, FALSE, ctx, pool, pool));
1626 const char *repos_relpath;
1628 if (! svn_path_is_url(path_or_url))
1630 SVN_ERR(svn_dirent_get_absolute(&path_or_url, path_or_url, pool));
1631 SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, NULL, NULL,
1632 ctx->wc_ctx, path_or_url,
1637 repos_relpath = svn_uri_skip_ancestor(repos_root, path_or_url, pool);
1639 SVN_ERR_ASSERT(repos_relpath != NULL); /* Or get_mergeinfo failed */
1642 mergeinfo = svn_hash_gets(mergeinfo_cat, repos_relpath);
1649 SVN_ERR(mergeinfo_relpaths_to_urls(mergeinfo_p, mergeinfo,
1650 repos_root, pool, pool));
1651 return SVN_NO_ERROR;
1655 svn_client__mergeinfo_log(svn_boolean_t finding_merged,
1656 const char *target_path_or_url,
1657 const svn_opt_revision_t *target_peg_revision,
1658 svn_mergeinfo_catalog_t *target_mergeinfo_catalog,
1659 const char *source_path_or_url,
1660 const svn_opt_revision_t *source_peg_revision,
1661 const svn_opt_revision_t *source_start_revision,
1662 const svn_opt_revision_t *source_end_revision,
1663 svn_log_entry_receiver_t log_receiver,
1664 void *log_receiver_baton,
1665 svn_boolean_t discover_changed_paths,
1667 const apr_array_header_t *revprops,
1668 svn_client_ctx_t *ctx,
1669 apr_pool_t *result_pool,
1670 apr_pool_t *scratch_pool)
1672 const char *log_target = NULL;
1673 const char *repos_root;
1674 const char *target_repos_relpath;
1675 svn_mergeinfo_catalog_t target_mergeinfo_cat;
1676 svn_ra_session_t *target_session = NULL;
1677 svn_client__pathrev_t *pathrev;
1679 /* A hash of paths, at or under TARGET_PATH_OR_URL, mapped to
1680 rangelists. Not technically mergeinfo, so not using the
1681 svn_mergeinfo_t type. */
1682 apr_hash_t *inheritable_subtree_merges;
1684 svn_mergeinfo_t source_history;
1685 svn_mergeinfo_t target_history;
1686 svn_rangelist_t *master_noninheritable_rangelist;
1687 svn_rangelist_t *master_inheritable_rangelist;
1688 apr_array_header_t *merge_source_fspaths =
1689 apr_array_make(scratch_pool, 1, sizeof(const char *));
1690 apr_hash_index_t *hi_catalog;
1691 apr_hash_index_t *hi;
1692 apr_pool_t *iterpool;
1693 svn_boolean_t oldest_revs_first = TRUE;
1694 apr_pool_t *subpool;
1696 /* We currently only support depth = empty | infinity. */
1697 if (depth != svn_depth_infinity && depth != svn_depth_empty)
1698 return svn_error_create(
1699 SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1700 _("Only depths 'infinity' and 'empty' are currently supported"));
1702 /* Validate and sanitize the incoming source operative revision range. */
1703 if (!((source_start_revision->kind == svn_opt_revision_unspecified) ||
1704 (source_start_revision->kind == svn_opt_revision_number) ||
1705 (source_start_revision->kind == svn_opt_revision_date) ||
1706 (source_start_revision->kind == svn_opt_revision_head)))
1707 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1708 if (!((source_end_revision->kind == svn_opt_revision_unspecified) ||
1709 (source_end_revision->kind == svn_opt_revision_number) ||
1710 (source_end_revision->kind == svn_opt_revision_date) ||
1711 (source_end_revision->kind == svn_opt_revision_head)))
1712 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1713 if ((source_end_revision->kind != svn_opt_revision_unspecified)
1714 && (source_start_revision->kind == svn_opt_revision_unspecified))
1715 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1716 if ((source_end_revision->kind == svn_opt_revision_unspecified)
1717 && (source_start_revision->kind != svn_opt_revision_unspecified))
1718 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1720 subpool = svn_pool_create(scratch_pool);
1722 /* We need the union of TARGET_PATH_OR_URL@TARGET_PEG_REVISION's mergeinfo
1723 and MERGE_SOURCE_URL's history. It's not enough to do path
1724 matching, because renames in the history of MERGE_SOURCE_URL
1725 throw that all in a tizzy. Of course, if there's no mergeinfo on
1726 the target, that vastly simplifies matters (we'll have nothing to
1728 /* This get_mergeinfo() call doubles as a mergeinfo capabilities check. */
1729 if (target_mergeinfo_catalog)
1731 if (*target_mergeinfo_catalog)
1733 /* The caller provided the mergeinfo catalog for
1734 TARGET_PATH_OR_URL, so we don't need to accquire
1735 it ourselves. We do need to get the repos_root
1736 though, because get_mergeinfo() won't do it for us. */
1737 target_mergeinfo_cat = *target_mergeinfo_catalog;
1738 SVN_ERR(svn_client__ra_session_from_path2(&target_session, &pathrev,
1739 target_path_or_url, NULL,
1740 target_peg_revision,
1741 target_peg_revision,
1743 SVN_ERR(svn_ra_get_repos_root2(target_session, &repos_root,
1748 /* The caller didn't provide the mergeinfo catalog for
1749 TARGET_PATH_OR_URL, but wants us to pass a copy back
1750 when we get it, so use RESULT_POOL. */
1751 SVN_ERR(get_mergeinfo(target_mergeinfo_catalog, &repos_root,
1752 target_path_or_url, target_peg_revision,
1753 depth == svn_depth_infinity, TRUE,
1754 ctx, result_pool, scratch_pool));
1755 target_mergeinfo_cat = *target_mergeinfo_catalog;
1760 /* The caller didn't provide the mergeinfo catalog for
1761 TARGET_PATH_OR_URL, nor does it want a copy, so we can use
1762 nothing but SCRATCH_POOL. */
1763 SVN_ERR(get_mergeinfo(&target_mergeinfo_cat, &repos_root,
1764 target_path_or_url, target_peg_revision,
1765 depth == svn_depth_infinity, TRUE,
1766 ctx, scratch_pool, scratch_pool));
1769 if (!svn_path_is_url(target_path_or_url))
1771 SVN_ERR(svn_dirent_get_absolute(&target_path_or_url,
1772 target_path_or_url, scratch_pool));
1773 SVN_ERR(svn_wc__node_get_repos_info(NULL, &target_repos_relpath,
1775 ctx->wc_ctx, target_path_or_url,
1776 scratch_pool, scratch_pool));
1780 target_repos_relpath = svn_uri_skip_ancestor(repos_root,
1784 /* TARGET_REPOS_REL should be non-NULL, else get_mergeinfo
1785 should have failed. */
1786 SVN_ERR_ASSERT(target_repos_relpath != NULL);
1789 if (!target_mergeinfo_cat)
1791 /* If we are looking for what has been merged and there is no
1792 mergeinfo then we already know the answer. If we are looking
1793 for eligible revisions then create a catalog with empty mergeinfo
1794 on the target. This is semantically equivalent to no mergeinfo
1795 and gives us something to combine with MERGE_SOURCE_URL's
1799 svn_pool_destroy(subpool);
1800 return SVN_NO_ERROR;
1804 target_mergeinfo_cat = apr_hash_make(scratch_pool);
1805 svn_hash_sets(target_mergeinfo_cat, target_repos_relpath,
1806 apr_hash_make(scratch_pool));
1810 /* Fetch the location history as mergeinfo, for the source branch
1811 * (between the given start and end revisions), and, if we're finding
1812 * merged revisions, then also for the entire target branch.
1814 * ### TODO: As the source and target must be in the same repository, we
1815 * should share a single session, tracking the two URLs separately. */
1817 svn_ra_session_t *source_session;
1818 svn_revnum_t start_rev, end_rev, youngest_rev = SVN_INVALID_REVNUM;
1820 if (! finding_merged)
1822 if (!target_session)
1823 SVN_ERR(svn_client__ra_session_from_path2(&target_session, &pathrev,
1824 target_path_or_url, NULL,
1825 target_peg_revision,
1826 target_peg_revision,
1828 SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history, NULL,
1832 target_session, ctx,
1836 SVN_ERR(svn_client__ra_session_from_path2(&source_session, &pathrev,
1837 source_path_or_url, NULL,
1838 source_peg_revision,
1839 source_peg_revision,
1841 SVN_ERR(svn_client__get_revision_number(&start_rev, &youngest_rev,
1842 ctx->wc_ctx, source_path_or_url,
1844 source_start_revision,
1846 SVN_ERR(svn_client__get_revision_number(&end_rev, &youngest_rev,
1847 ctx->wc_ctx, source_path_or_url,
1849 source_end_revision,
1851 SVN_ERR(svn_client__get_history_as_mergeinfo(&source_history, NULL,
1853 MAX(end_rev, start_rev),
1854 MIN(end_rev, start_rev),
1855 source_session, ctx,
1857 if (start_rev > end_rev)
1858 oldest_revs_first = FALSE;
1860 /* Close the source and target sessions. */
1861 svn_pool_destroy(subpool);
1864 /* Separate the explicit or inherited mergeinfo on TARGET_PATH_OR_URL,
1865 and possibly its explicit subtree mergeinfo, into their
1866 inheritable and non-inheritable parts. */
1867 master_noninheritable_rangelist = apr_array_make(scratch_pool, 64,
1868 sizeof(svn_merge_range_t *));
1869 master_inheritable_rangelist = apr_array_make(scratch_pool, 64,
1870 sizeof(svn_merge_range_t *));
1871 inheritable_subtree_merges = apr_hash_make(scratch_pool);
1873 iterpool = svn_pool_create(scratch_pool);
1875 for (hi_catalog = apr_hash_first(scratch_pool, target_mergeinfo_cat);
1877 hi_catalog = apr_hash_next(hi_catalog))
1879 svn_mergeinfo_t subtree_mergeinfo = svn__apr_hash_index_val(hi_catalog);
1880 svn_mergeinfo_t subtree_history;
1881 svn_mergeinfo_t subtree_source_history;
1882 svn_mergeinfo_t subtree_inheritable_mergeinfo;
1883 svn_mergeinfo_t subtree_noninheritable_mergeinfo;
1884 svn_mergeinfo_t merged_noninheritable;
1885 svn_mergeinfo_t merged;
1886 const char *subtree_path = svn__apr_hash_index_key(hi_catalog);
1887 svn_boolean_t is_subtree = strcmp(subtree_path,
1888 target_repos_relpath) != 0;
1889 svn_pool_clear(iterpool);
1893 /* If SUBTREE_PATH is a proper subtree of TARGET_PATH_OR_URL
1894 then make a copy of SOURCE_HISTORY that is path adjusted
1896 const char *subtree_rel_path =
1897 subtree_path + strlen(target_repos_relpath) + 1;
1899 SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
1900 &subtree_source_history, source_history,
1901 subtree_rel_path, scratch_pool, scratch_pool));
1903 if (!finding_merged)
1904 SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
1905 &subtree_history, target_history,
1906 subtree_rel_path, scratch_pool, scratch_pool));
1910 subtree_source_history = source_history;
1911 if (!finding_merged)
1912 subtree_history = target_history;
1915 if (!finding_merged)
1917 svn_mergeinfo_t merged_via_history;
1918 SVN_ERR(svn_mergeinfo_intersect2(&merged_via_history,
1920 subtree_source_history, TRUE,
1921 scratch_pool, iterpool));
1922 SVN_ERR(svn_mergeinfo_merge2(subtree_mergeinfo,
1924 scratch_pool, scratch_pool));
1927 SVN_ERR(svn_mergeinfo_inheritable2(&subtree_inheritable_mergeinfo,
1928 subtree_mergeinfo, NULL,
1931 TRUE, scratch_pool, iterpool));
1932 SVN_ERR(svn_mergeinfo_inheritable2(&subtree_noninheritable_mergeinfo,
1933 subtree_mergeinfo, NULL,
1936 FALSE, scratch_pool, iterpool));
1938 /* Find the intersection of the non-inheritable part of
1939 SUBTREE_MERGEINFO and SOURCE_HISTORY. svn_mergeinfo_intersect2()
1940 won't consider non-inheritable and inheritable ranges
1941 intersecting unless we ignore inheritance, but in doing so the
1942 resulting intersections have all inheritable ranges. To get
1943 around this we set the inheritance on the result to all
1945 SVN_ERR(svn_mergeinfo_intersect2(&merged_noninheritable,
1946 subtree_noninheritable_mergeinfo,
1947 subtree_source_history, FALSE,
1948 scratch_pool, iterpool));
1949 svn_mergeinfo__set_inheritance(merged_noninheritable, FALSE,
1952 /* Keep track of all ranges partially merged to any and all
1954 SVN_ERR(svn_rangelist__merge_many(master_noninheritable_rangelist,
1955 merged_noninheritable,
1956 scratch_pool, iterpool));
1958 /* Find the intersection of the inheritable part of TGT_MERGEINFO
1959 and SOURCE_HISTORY. */
1960 SVN_ERR(svn_mergeinfo_intersect2(&merged,
1961 subtree_inheritable_mergeinfo,
1962 subtree_source_history, FALSE,
1963 scratch_pool, iterpool));
1965 /* Keep track of all ranges fully merged to any and all
1967 if (apr_hash_count(merged))
1969 /* The inheritable rangelist merged from SUBTREE_SOURCE_HISTORY
1971 svn_rangelist_t *subtree_merged_rangelist =
1972 apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
1974 SVN_ERR(svn_rangelist__merge_many(master_inheritable_rangelist,
1975 merged, scratch_pool, iterpool));
1976 SVN_ERR(svn_rangelist__merge_many(subtree_merged_rangelist,
1977 merged, scratch_pool, iterpool));
1979 svn_hash_sets(inheritable_subtree_merges, subtree_path,
1980 subtree_merged_rangelist);
1984 /* Map SUBTREE_PATH to an empty rangelist if there was nothing
1985 fully merged. e.g. Only empty or non-inheritable mergeinfo
1986 on the subtree or mergeinfo unrelated to the source. */
1987 svn_hash_sets(inheritable_subtree_merges, subtree_path,
1988 apr_array_make(scratch_pool, 0,
1989 sizeof(svn_merge_range_t *)));
1993 /* Make sure every range in MASTER_INHERITABLE_RANGELIST is fully merged to
1994 each subtree (including the target itself). Any revisions which don't
1995 exist in *every* subtree are *potentially* only partially merged to the
1996 tree rooted at TARGET_PATH_OR_URL, so move those revisions to
1997 MASTER_NONINHERITABLE_RANGELIST. It may turn out that that a revision
1998 was merged to the only subtree it affects, but we need to examine the
1999 logs to make this determination (which will be done by
2000 logs_for_mergeinfo_rangelist). */
2001 if (master_inheritable_rangelist->nelts)
2003 for (hi = apr_hash_first(scratch_pool, inheritable_subtree_merges);
2005 hi = apr_hash_next(hi))
2007 svn_rangelist_t *deleted_rangelist;
2008 svn_rangelist_t *added_rangelist;
2009 svn_rangelist_t *subtree_merged_rangelist =
2010 svn__apr_hash_index_val(hi);
2012 svn_pool_clear(iterpool);
2014 SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
2015 master_inheritable_rangelist,
2016 subtree_merged_rangelist, TRUE,
2019 if (deleted_rangelist->nelts)
2021 svn_rangelist__set_inheritance(deleted_rangelist, FALSE);
2022 SVN_ERR(svn_rangelist_merge2(master_noninheritable_rangelist,
2024 scratch_pool, iterpool));
2025 SVN_ERR(svn_rangelist_remove(&master_inheritable_rangelist,
2027 master_inheritable_rangelist,
2036 /* Roll all the merged revisions into one rangelist. */
2037 SVN_ERR(svn_rangelist_merge2(master_inheritable_rangelist,
2038 master_noninheritable_rangelist,
2039 scratch_pool, scratch_pool));
2044 /* Create the starting rangelist for what might be eligible. */
2045 svn_rangelist_t *source_master_rangelist =
2046 apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
2048 SVN_ERR(svn_rangelist__merge_many(source_master_rangelist,
2050 scratch_pool, scratch_pool));
2052 /* From what might be eligible subtract what we know is
2053 partially merged and then merge that back. */
2054 SVN_ERR(svn_rangelist_remove(&source_master_rangelist,
2055 master_noninheritable_rangelist,
2056 source_master_rangelist,
2057 FALSE, scratch_pool));
2058 SVN_ERR(svn_rangelist_merge2(source_master_rangelist,
2059 master_noninheritable_rangelist,
2060 scratch_pool, scratch_pool));
2061 SVN_ERR(svn_rangelist_remove(&master_inheritable_rangelist,
2062 master_inheritable_rangelist,
2063 source_master_rangelist,
2064 TRUE, scratch_pool));
2067 /* Nothing merged? Not even when considering shared history if
2068 looking for eligible revisions (i.e. !FINDING_MERGED)? Then there
2069 is nothing more to do. */
2070 if (! master_inheritable_rangelist->nelts)
2072 svn_pool_destroy(iterpool);
2073 return SVN_NO_ERROR;
2077 /* Determine the correct (youngest) target for 'svn log'. */
2078 svn_merge_range_t *youngest_range
2079 = APR_ARRAY_IDX(master_inheritable_rangelist,
2080 master_inheritable_rangelist->nelts - 1,
2081 svn_merge_range_t *);
2082 svn_rangelist_t *youngest_rangelist =
2083 svn_rangelist__initialize(youngest_range->end - 1,
2084 youngest_range->end,
2085 youngest_range->inheritable,
2088 for (hi = apr_hash_first(scratch_pool, source_history);
2090 hi = apr_hash_next(hi))
2092 const char *key = svn__apr_hash_index_key(hi);
2093 svn_rangelist_t *subtree_merged_rangelist =
2094 svn__apr_hash_index_val(hi);
2095 svn_rangelist_t *intersecting_rangelist;
2097 svn_pool_clear(iterpool);
2098 SVN_ERR(svn_rangelist_intersect(&intersecting_rangelist,
2100 subtree_merged_rangelist,
2103 APR_ARRAY_PUSH(merge_source_fspaths, const char *) = key;
2105 if (intersecting_rangelist->nelts)
2110 svn_pool_destroy(iterpool);
2112 /* Step 4: Finally, we run 'svn log' to drive our log receiver, but
2113 using a receiver filter to only allow revisions to pass through
2114 that are in our rangelist. */
2115 log_target = svn_path_url_add_component2(repos_root, log_target + 1,
2118 SVN_ERR(logs_for_mergeinfo_rangelist(log_target, merge_source_fspaths,
2120 master_inheritable_rangelist,
2122 target_mergeinfo_cat,
2123 svn_fspath__join("/",
2124 target_repos_relpath,
2126 discover_changed_paths,
2128 log_receiver, log_receiver_baton,
2129 ctx, scratch_pool));
2130 return SVN_NO_ERROR;
2134 svn_client_mergeinfo_log2(svn_boolean_t finding_merged,
2135 const char *target_path_or_url,
2136 const svn_opt_revision_t *target_peg_revision,
2137 const char *source_path_or_url,
2138 const svn_opt_revision_t *source_peg_revision,
2139 const svn_opt_revision_t *source_start_revision,
2140 const svn_opt_revision_t *source_end_revision,
2141 svn_log_entry_receiver_t log_receiver,
2142 void *log_receiver_baton,
2143 svn_boolean_t discover_changed_paths,
2145 const apr_array_header_t *revprops,
2146 svn_client_ctx_t *ctx,
2147 apr_pool_t *scratch_pool)
2149 return svn_client__mergeinfo_log(finding_merged, target_path_or_url,
2150 target_peg_revision, NULL,
2151 source_path_or_url, source_peg_revision,
2152 source_start_revision, source_end_revision,
2153 log_receiver, log_receiver_baton,
2154 discover_changed_paths, depth, revprops,
2155 ctx, scratch_pool, scratch_pool);
2159 svn_client_suggest_merge_sources(apr_array_header_t **suggestions,
2160 const char *path_or_url,
2161 const svn_opt_revision_t *peg_revision,
2162 svn_client_ctx_t *ctx,
2165 const char *repos_root;
2166 const char *copyfrom_path;
2167 apr_array_header_t *list;
2168 svn_revnum_t copyfrom_rev;
2169 svn_mergeinfo_catalog_t mergeinfo_cat;
2170 svn_mergeinfo_t mergeinfo;
2171 apr_hash_index_t *hi;
2173 list = apr_array_make(pool, 1, sizeof(const char *));
2175 /* In our ideal algorithm, the list of recommendations should be
2178 1. The most recent existing merge source.
2179 2. The copyfrom source (which will also be listed as a merge
2180 source if the copy was made with a 1.5+ client and server).
2181 3. All other merge sources, most recent to least recent.
2183 However, determining the order of application of merge sources
2184 requires a new RA API. Until such an API is available, our
2187 1. The copyfrom source.
2188 2. All remaining merge sources (unordered).
2191 /* ### TODO: Share ra_session batons to improve efficiency? */
2192 SVN_ERR(get_mergeinfo(&mergeinfo_cat, &repos_root, path_or_url,
2193 peg_revision, FALSE, FALSE, ctx, pool, pool));
2195 if (mergeinfo_cat && apr_hash_count(mergeinfo_cat))
2197 /* We asked only for the PATH_OR_URL's mergeinfo, not any of its
2198 descendants. So if there is anything in the catalog it is the
2199 mergeinfo for PATH_OR_URL. */
2200 mergeinfo = svn__apr_hash_index_val(apr_hash_first(pool, mergeinfo_cat));
2207 SVN_ERR(svn_client__get_copy_source(©from_path, ©from_rev,
2208 path_or_url, peg_revision, ctx,
2212 APR_ARRAY_PUSH(list, const char *) =
2213 svn_path_url_add_component2(repos_root, copyfrom_path, pool);
2218 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2220 const char *rel_path = svn__apr_hash_index_key(hi);
2222 if (copyfrom_path == NULL || strcmp(rel_path, copyfrom_path) != 0)
2223 APR_ARRAY_PUSH(list, const char *) = \
2224 svn_path_url_add_component2(repos_root, rel_path + 1, pool);
2228 *suggestions = list;
2229 return SVN_NO_ERROR;
2233 svn_client__mergeinfo_status(svn_boolean_t *mergeinfo_changes,
2234 svn_wc_context_t *wc_ctx,
2235 const char *local_abspath,
2236 apr_pool_t *scratch_pool)
2238 apr_array_header_t *propchanges;
2241 *mergeinfo_changes = FALSE;
2243 SVN_ERR(svn_wc_get_prop_diffs2(&propchanges, NULL, wc_ctx,
2244 local_abspath, scratch_pool, scratch_pool));
2246 for (i = 0; i < propchanges->nelts; i++)
2248 svn_prop_t prop = APR_ARRAY_IDX(propchanges, i, svn_prop_t);
2249 if (strcmp(prop.name, SVN_PROP_MERGEINFO) == 0)
2251 *mergeinfo_changes = TRUE;
2256 return SVN_NO_ERROR;