]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_client/mergeinfo.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / subversion / subversion / libsvn_client / mergeinfo.c
1 /*
2  * mergeinfo.c :  merge history functions for the libsvn_client library
3  *
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
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
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
20  *    under the License.
21  * ====================================================================
22  */
23
24 #include <apr_pools.h>
25 #include <apr_strings.h>
26
27 #include "svn_pools.h"
28 #include "svn_dirent_uri.h"
29 #include "svn_path.h"
30 #include "svn_string.h"
31 #include "svn_opt.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"
37 #include "svn_ra.h"
38 #include "svn_client.h"
39 #include "svn_hash.h"
40
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"
47 #include "client.h"
48 #include "mergeinfo.h"
49 #include "svn_private_config.h"
50
51
52
53 svn_client__merge_path_t *
54 svn_client__merge_path_dup(const svn_client__merge_path_t *old,
55                            apr_pool_t *pool)
56 {
57   svn_client__merge_path_t *new = apr_pmemdup(pool, old, sizeof(*old));
58
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,
64                                                  pool);
65   if (new->implicit_mergeinfo)
66     new->implicit_mergeinfo = svn_mergeinfo_dup(old->implicit_mergeinfo,
67                                                 pool);
68
69   return new;
70 }
71
72 svn_client__merge_path_t *
73 svn_client__merge_path_create(const char *abspath,
74                               apr_pool_t *pool)
75 {
76   svn_client__merge_path_t *result = apr_pcalloc(pool, sizeof(*result));
77
78   result->abspath = apr_pstrdup(pool, abspath);
79   return result;
80 }
81
82 svn_error_t *
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)
88 {
89   const svn_string_t *propval;
90
91   *mergeinfo = NULL;
92
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));
98   if (propval)
99     SVN_ERR(svn_mergeinfo_parse(mergeinfo, propval->data, result_pool));
100
101   return SVN_NO_ERROR;
102 }
103
104 svn_error_t *
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)
110 {
111   svn_string_t *mergeinfo_str = NULL;
112   svn_boolean_t mergeinfo_changes = FALSE;
113
114   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
115
116   /* Convert MERGEINFO (if any) into text for storage as a property value. */
117   if (mergeinfo)
118     SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_str, mergeinfo, scratch_pool));
119
120   if (do_notification && ctx->notify_func2)
121     SVN_ERR(svn_client__mergeinfo_status(&mergeinfo_changes, ctx->wc_ctx,
122                                          local_abspath, scratch_pool));
123
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 */,
132                            scratch_pool));
133
134   if (do_notification && ctx->notify_func2)
135     {
136       svn_wc_notify_t *notify =
137         svn_wc_create_notify(local_abspath,
138                              svn_wc_notify_merge_record_info,
139                              scratch_pool);
140       if (mergeinfo_changes)
141         notify->prop_state = svn_wc_notify_state_merged;
142       else
143         notify->prop_state = svn_wc_notify_state_changed;
144
145       ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
146     }
147
148   return SVN_NO_ERROR;
149 }
150
151 svn_error_t *
152 svn_client__record_wc_mergeinfo_catalog(apr_hash_t *result_catalog,
153                                         svn_client_ctx_t *ctx,
154                                         apr_pool_t *scratch_pool)
155 {
156   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
157
158   if (apr_hash_count(result_catalog))
159     {
160       int i;
161       apr_array_header_t *sorted_cat =
162         svn_sort__hash(result_catalog, svn_sort_compare_items_as_paths,
163                        scratch_pool);
164
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++)
168         {
169           svn_sort__item_t elt = APR_ARRAY_IDX(sorted_cat, i,
170                                                svn_sort__item_t);
171           svn_error_t *err;
172
173           svn_pool_clear(iterpool);
174           err = svn_client__record_wc_mergeinfo(elt.key, elt.value, TRUE,
175                                                 ctx, iterpool);
176
177           if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
178             {
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
184                  continue... */
185               svn_error_clear(err);
186             }
187           else
188             {
189               SVN_ERR(err);
190             }
191         }
192     }
193   svn_pool_destroy(iterpool);
194   return SVN_NO_ERROR;
195 }
196
197 /*-----------------------------------------------------------------------*/
198 \f
199 /*** Retrieving mergeinfo. ***/
200
201 svn_error_t *
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)
212 {
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;
218
219   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
220   if (limit_abspath)
221     SVN_ERR_ASSERT(svn_dirent_is_absolute(limit_abspath));
222
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));
228
229   iterpool = svn_pool_create(scratch_pool);
230   while (TRUE)
231     {
232       svn_pool_clear(iterpool);
233
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)
237         {
238           wc_mergeinfo = NULL;
239           inherit = svn_mergeinfo_inherited;
240         }
241       else
242         {
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,
249                                                          ctx->wc_ctx,
250                                                          local_abspath,
251                                                          result_pool,
252                                                          iterpool);
253           if ((ignore_invalid_mergeinfo || walk_relpath [0] != '\0')
254               && err
255               && err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
256             {
257               svn_error_clear(err);
258               wc_mergeinfo = apr_hash_make(result_pool);
259               break;
260             }
261           else
262             {
263               SVN_ERR(err);
264             }
265         }
266
267       if (wc_mergeinfo == NULL &&
268           inherit != svn_mergeinfo_explicit &&
269           !svn_dirent_is_root(local_abspath, strlen(local_abspath)))
270         {
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;
275
276           /* Don't look any higher than the limit path. */
277           if (limit_abspath && strcmp(limit_abspath, local_abspath) == 0)
278             break;
279
280           /* If we've reached the root of the working copy don't look any
281              higher. */
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)
285             break;
286
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,
290                                                               iterpool),
291                                           walk_relpath, result_pool);
292           local_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
293
294           SVN_ERR(svn_wc__node_get_base(NULL, &parent_base_rev, NULL, NULL,
295                                         NULL, NULL,
296                                         ctx->wc_ctx, local_abspath,
297                                         TRUE, FALSE,
298                                         scratch_pool, scratch_pool));
299
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,
304                                                 NULL, NULL,
305                                                 ctx->wc_ctx, local_abspath,
306                                                 scratch_pool,
307                                                 scratch_pool));
308
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))
317             break;
318
319           /* We haven't yet risen above the root of the WC. */
320           continue;
321         }
322       break;
323     }
324
325   svn_pool_destroy(iterpool);
326
327   if (svn_path_is_empty(walk_relpath))
328     {
329       /* Mergeinfo is explicit. */
330       inherited = FALSE;
331       *mergeinfo = wc_mergeinfo;
332     }
333   else
334     {
335       /* Mergeinfo may be inherited. */
336       if (wc_mergeinfo)
337         {
338           inherited = TRUE;
339           SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(mergeinfo,
340                                                          wc_mergeinfo,
341                                                          walk_relpath,
342                                                          result_pool,
343                                                          scratch_pool));
344         }
345       else
346         {
347           inherited = FALSE;
348           *mergeinfo = NULL;
349         }
350     }
351
352   if (walked_path)
353     *walked_path = walk_relpath;
354
355   /* Remove non-inheritable mergeinfo and paths mapped to empty ranges
356      which may occur if WCPATH's mergeinfo is not explicit. */
357   if (inherited
358       && apr_hash_count(*mergeinfo)) /* Nothing to do for empty mergeinfo. */
359     {
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);
364     }
365
366   if (inherited_p)
367     *inherited_p = inherited;
368
369   return SVN_NO_ERROR;
370 }
371
372 svn_error_t *
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)
384 {
385   const char *target_repos_relpath;
386   svn_mergeinfo_t mergeinfo;
387   const char *repos_root;
388
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,
392                                       &repos_root, NULL,
393                                       ctx->wc_ctx, local_abspath,
394                                       scratch_pool, scratch_pool));
395
396   /* Get the mergeinfo for the LOCAL_ABSPATH target and set *INHERITED and
397      *WALKED_PATH. */
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));
402
403   /* Add any explicit/inherited mergeinfo for LOCAL_ABSPATH to
404      *MERGEINFO_CAT. */
405   if (mergeinfo)
406     {
407       *mergeinfo_cat = apr_hash_make(result_pool);
408       svn_hash_sets(*mergeinfo_cat,
409                     apr_pstrdup(result_pool, target_repos_relpath), mergeinfo);
410     }
411
412   /* If LOCAL_ABSPATH is a directory and we want the subtree mergeinfo too,
413      then get it.
414
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)
418     {
419       apr_hash_t *mergeinfo_props;
420       apr_hash_index_t *hi;
421
422       SVN_ERR(svn_wc__prop_retrieve_recursive(&mergeinfo_props,
423                                               ctx->wc_ctx, local_abspath,
424                                               SVN_PROP_MERGEINFO,
425                                               scratch_pool, scratch_pool));
426
427       /* Convert *mergeinfo_props into a proper svn_mergeinfo_catalog_t */
428       for (hi = apr_hash_first(scratch_pool, mergeinfo_props);
429            hi;
430            hi = apr_hash_next(hi))
431         {
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;
436
437           if (strcmp(node_abspath, local_abspath) == 0)
438             continue; /* Already parsed in svn_client__get_wc_mergeinfo */
439
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));
443
444           SVN_ERR(svn_mergeinfo_parse(&subtree_mergeinfo, propval->data,
445                                       result_pool));
446
447           /* If the target had no explicit/inherited mergeinfo and this is the
448              first subtree with mergeinfo found, then the catalog will still
449              be NULL. */
450           if (*mergeinfo_cat == NULL)
451             *mergeinfo_cat = apr_hash_make(result_pool);
452
453           svn_hash_sets(*mergeinfo_cat, repos_relpath, subtree_mergeinfo);
454         }
455     }
456
457   return SVN_NO_ERROR;
458 }
459
460 svn_error_t *
461 svn_client__get_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo,
462                                 svn_ra_session_t *ra_session,
463                                 const char *url,
464                                 svn_revnum_t rev,
465                                 svn_mergeinfo_inheritance_t inherit,
466                                 svn_boolean_t squelch_incapable,
467                                 apr_pool_t *pool)
468 {
469   svn_mergeinfo_catalog_t tgt_mergeinfo_cat;
470
471   *target_mergeinfo = NULL;
472
473   SVN_ERR(svn_client__get_repos_mergeinfo_catalog(&tgt_mergeinfo_cat,
474                                                   ra_session,
475                                                   url, rev, inherit,
476                                                   squelch_incapable, FALSE,
477                                                   pool, pool));
478
479   if (tgt_mergeinfo_cat && apr_hash_count(tgt_mergeinfo_cat))
480     {
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. */
484       *target_mergeinfo =
485         svn__apr_hash_index_val(apr_hash_first(pool, tgt_mergeinfo_cat));
486
487     }
488
489   return SVN_NO_ERROR;
490 }
491
492 svn_error_t *
493 svn_client__get_repos_mergeinfo_catalog(svn_mergeinfo_catalog_t *mergeinfo_cat,
494                                         svn_ra_session_t *ra_session,
495                                         const char *url,
496                                         svn_revnum_t rev,
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)
502 {
503   svn_error_t *err;
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;
508
509   APR_ARRAY_PUSH(rel_paths, const char *) = "";
510
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));
518   if (err)
519     {
520       if (squelch_incapable && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
521         {
522           svn_error_clear(err);
523           *mergeinfo_cat = NULL;
524           return SVN_NO_ERROR;
525         }
526       else
527         return svn_error_trace(err);
528     }
529
530   if (repos_mergeinfo_cat == NULL)
531     {
532       *mergeinfo_cat = NULL;
533     }
534   else
535     {
536       const char *session_relpath;
537
538       SVN_ERR(svn_ra_get_path_relative_to_root(ra_session, &session_relpath,
539                                                url, scratch_pool));
540
541       if (session_relpath[0] == '\0')
542         *mergeinfo_cat = repos_mergeinfo_cat;
543       else
544         SVN_ERR(svn_mergeinfo__add_prefix_to_catalog(mergeinfo_cat,
545                                                      repos_mergeinfo_cat,
546                                                      session_relpath,
547                                                      result_pool,
548                                                      scratch_pool));
549     }
550   return SVN_NO_ERROR;
551 }
552
553
554 svn_error_t *
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,
563                                       apr_pool_t *pool)
564 {
565   svn_mergeinfo_catalog_t tgt_mergeinfo_cat;
566
567   *target_mergeinfo = NULL;
568
569   SVN_ERR(svn_client__get_wc_or_repos_mergeinfo_catalog(&tgt_mergeinfo_cat,
570                                                         inherited, from_repos,
571                                                         FALSE,
572                                                         repos_only,
573                                                         FALSE, inherit,
574                                                         ra_session,
575                                                         target_wcpath, ctx,
576                                                         pool, pool));
577   if (tgt_mergeinfo_cat && apr_hash_count(tgt_mergeinfo_cat))
578     {
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. */
584       *target_mergeinfo =
585         svn__apr_hash_index_val(apr_hash_first(pool, tgt_mergeinfo_cat));
586
587     }
588
589   return SVN_NO_ERROR;
590 }
591
592 svn_error_t *
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)
606 {
607   const char *url;
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;
614
615   SVN_ERR(svn_dirent_get_absolute(&local_abspath, target_wcpath,
616                                   scratch_pool));
617
618   if (from_repos)
619     *from_repos = FALSE;
620
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
624      TARGET_WCPATH. */
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));
629
630   if (repos_relpath)
631     url = svn_path_url_add_component2(repos_root, repos_relpath, scratch_pool);
632   else
633     url = NULL;
634
635   if (!repos_only)
636     {
637       svn_boolean_t inherited;
638       SVN_ERR(svn_client__get_wc_mergeinfo_catalog(&target_mergeinfo_cat_wc,
639                                                    &inherited,
640                                                    include_descendants,
641                                                    inherit,
642                                                    local_abspath,
643                                                    NULL, NULL,
644                                                    ignore_invalid_mergeinfo,
645                                                    ctx,
646                                                    result_pool,
647                                                    scratch_pool));
648       if (inherited_p)
649         *inherited_p = inherited;
650
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
653          repository. */
654       if (! (inherited
655              || (inherit == svn_mergeinfo_explicit)
656              || (repos_relpath
657                  && target_mergeinfo_cat_wc
658                  && svn_hash_gets(target_mergeinfo_cat_wc, repos_relpath))))
659         {
660           repos_only = TRUE;
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;
664         }
665     }
666
667   if (repos_only)
668     {
669       /* No need to check the repos if this is a local addition. */
670       if (url != NULL)
671         {
672           apr_hash_t *original_props;
673
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))
681             {
682               apr_pool_t *sesspool = NULL;
683
684               if (! ra_session)
685                 {
686                   sesspool = svn_pool_create(scratch_pool);
687                   SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL,
688                                                       ctx,
689                                                       sesspool, sesspool));
690                 }
691
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));
697
698               if (target_mergeinfo_cat_repos
699                   && svn_hash_gets(target_mergeinfo_cat_repos, repos_relpath))
700                 {
701                   if (inherited_p)
702                     *inherited_p = TRUE;
703                   if (from_repos)
704                     *from_repos = TRUE;
705                 }
706
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. */
710               if (sesspool)
711                 {
712                   svn_pool_destroy(sesspool);
713                 }
714             }
715         }
716     }
717
718   /* Combine the mergeinfo from the working copy and repository as needed. */
719   if (target_mergeinfo_cat_wc)
720     {
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));
726     }
727   else if (target_mergeinfo_cat_repos)
728     {
729       *target_mergeinfo_catalog = target_mergeinfo_cat_repos;
730     }
731   else
732     {
733       *target_mergeinfo_catalog = NULL;
734     }
735
736   return SVN_NO_ERROR;
737 }
738
739
740 svn_error_t *
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,
748                                       apr_pool_t *pool)
749 {
750   apr_array_header_t *segments;
751
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))
756     range_oldest = 0;
757
758   SVN_ERR(svn_client__repos_location_segments(&segments, ra_session,
759                                               pathrev->url, pathrev->rev,
760                                               range_youngest, range_oldest,
761                                               ctx, pool));
762
763   if (has_rev_zero_history)
764     {
765       *has_rev_zero_history = FALSE;
766         if (segments->nelts)
767           {
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;
772           }
773     }
774
775   SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(mergeinfo_p, segments, pool));
776
777   return SVN_NO_ERROR;
778 }
779
780
781 /*-----------------------------------------------------------------------*/
782 \f
783 /*** Eliding mergeinfo. ***/
784
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.
791
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.
799
800    If PATH_SUFFIX and PARENT_MERGEINFO are not NULL append PATH_SUFFIX
801    to each path in PARENT_MERGEINFO before performing the comparison. */
802 static svn_error_t *
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)
808 {
809   /* Easy out: No child mergeinfo to elide. */
810   if (child_mergeinfo == NULL)
811     {
812       *elides = FALSE;
813     }
814   else if (apr_hash_count(child_mergeinfo) == 0)
815     {
816       /* Empty mergeinfo elides to empty mergeinfo or to "nothing",
817          i.e. it isn't overriding any parent. Otherwise it doesn't
818          elide. */
819       *elides = (!parent_mergeinfo || apr_hash_count(parent_mergeinfo) == 0);
820     }
821   else if (!parent_mergeinfo || apr_hash_count(parent_mergeinfo) == 0)
822     {
823       /* Non-empty mergeinfo never elides to empty mergeinfo
824          or no mergeinfo. */
825       *elides = FALSE;
826     }
827   else
828     {
829       /* Both CHILD_MERGEINFO and PARENT_MERGEINFO are non-NULL and
830          non-empty. */
831       svn_mergeinfo_t path_tweaked_parent_mergeinfo;
832
833       /* If we need to adjust the paths in PARENT_MERGEINFO do it now. */
834       if (path_suffix)
835         SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
836                   &path_tweaked_parent_mergeinfo, parent_mergeinfo,
837                   path_suffix, scratch_pool, scratch_pool));
838       else
839         path_tweaked_parent_mergeinfo = parent_mergeinfo;
840
841       SVN_ERR(svn_mergeinfo__equals(elides,
842                                     path_tweaked_parent_mergeinfo,
843                                     child_mergeinfo, TRUE, scratch_pool));
844     }
845
846   return SVN_NO_ERROR;
847 }
848
849 /* Helper for svn_client__elide_mergeinfo().
850
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.
855
856    If elision does occur, then remove the mergeinfo for LOCAL_ABSPATH.
857
858    If CHILD_MERGEINFO is NULL, do nothing.
859
860    Use SCRATCH_POOL for temporary allocations.
861 */
862 static svn_error_t *
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)
868 {
869   svn_boolean_t elides;
870
871   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
872
873   SVN_ERR(should_elide_mergeinfo(&elides,
874                                  parent_mergeinfo, child_mergeinfo, NULL,
875                                  scratch_pool));
876
877   if (elides)
878     {
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 */,
883                                scratch_pool));
884
885       if (ctx->notify_func2)
886         {
887           svn_wc_notify_t *notify;
888
889           notify = svn_wc_create_notify(local_abspath,
890                                         svn_wc_notify_merge_elide_info,
891                                         scratch_pool);
892           ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
893
894           notify = svn_wc_create_notify(local_abspath,
895                                         svn_wc_notify_update_update,
896                                         scratch_pool);
897           notify->prop_state = svn_wc_notify_state_changed;
898
899           ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
900         }
901     }
902
903   return SVN_NO_ERROR;
904 }
905
906
907 svn_error_t *
908 svn_client__elide_mergeinfo(const char *target_abspath,
909                             const char *wc_elision_limit_abspath,
910                             svn_client_ctx_t *ctx,
911                             apr_pool_t *pool)
912 {
913   const char *limit_abspath = wc_elision_limit_abspath;
914
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));
917
918   /* Check for first easy out: We are already at the limit path. */
919   if (!limit_abspath
920       || strcmp(target_abspath, limit_abspath) != 0)
921     {
922       svn_mergeinfo_t target_mergeinfo;
923       svn_mergeinfo_t mergeinfo = NULL;
924       svn_boolean_t inherited;
925       const char *walk_path;
926       svn_error_t *err;
927
928       /* Get the TARGET_WCPATH's explicit mergeinfo. */
929       err = svn_client__get_wc_mergeinfo(&target_mergeinfo, &inherited,
930                                          svn_mergeinfo_inherited,
931                                          target_abspath,
932                                          limit_abspath,
933                                          &walk_path, FALSE,
934                                          ctx, pool, pool);
935       if (err)
936         {
937           if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
938             {
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);
943               return SVN_NO_ERROR;
944             }
945           else
946             {
947               return svn_error_trace(err);
948             }
949         }
950
951      /* If TARGET_WCPATH has no explicit mergeinfo, there's nothing to
952          elide, we're done. */
953       if (inherited || target_mergeinfo == NULL)
954         return SVN_NO_ERROR;
955
956       /* Get TARGET_WCPATH's inherited mergeinfo from the WC. */
957       err = svn_client__get_wc_mergeinfo(&mergeinfo, NULL,
958                                          svn_mergeinfo_nearest_ancestor,
959                                          target_abspath,
960                                          limit_abspath,
961                                          &walk_path, FALSE, ctx, pool, pool);
962       if (err)
963         {
964           if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
965             {
966               /* Issue #3896 again, but invalid mergeinfo is inherited. */
967               svn_error_clear(err);
968               return SVN_NO_ERROR;
969             }
970           else
971             {
972               return svn_error_trace(err);
973             }
974         }
975
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)
980         {
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);
985           if (err)
986             {
987               if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
988                 {
989                   /* Issue #3896 again, but invalid mergeinfo is inherited
990                      from the repository. */
991                   svn_error_clear(err);
992                   return SVN_NO_ERROR;
993                 }
994               else
995                 {
996                   return svn_error_trace(err);
997                 }
998             }
999         }
1000
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;
1005
1006       SVN_ERR(elide_mergeinfo(mergeinfo, target_mergeinfo, target_abspath,
1007                               ctx, pool));
1008     }
1009   return SVN_NO_ERROR;
1010 }
1011
1012
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.
1018
1019    If no mergeinfo is found then set *MERGEINFO_CATALOG to NULL.
1020
1021    Set *REPOS_ROOT to the root URL of the repository associated with
1022    PATH_OR_URL.
1023
1024    If RA_SESSION is NOT NULL and PATH_OR_URL refers to a URL, RA_SESSION
1025    (which must be of the repository containing PATH_OR_URL) will be used
1026    instead of a temporary RA session. Caller is responsible for reparenting
1027    the session if it wants to use it after the call.
1028
1029    Allocate *MERGEINFO_CATALOG and all its contents in RESULT_POOL.  Use
1030    SCRATCH_POOL for all temporary allocations.
1031
1032    Return SVN_ERR_UNSUPPORTED_FEATURE if the server does not support
1033    Merge Tracking.  */
1034 static svn_error_t *
1035 get_mergeinfo(svn_mergeinfo_catalog_t *mergeinfo_catalog,
1036               const char **repos_root,
1037               const char *path_or_url,
1038               const svn_opt_revision_t *peg_revision,
1039               svn_boolean_t include_descendants,
1040               svn_boolean_t ignore_invalid_mergeinfo,
1041               svn_client_ctx_t *ctx,
1042               svn_ra_session_t *ra_session,
1043               apr_pool_t *result_pool,
1044               apr_pool_t *scratch_pool)
1045 {
1046   const char *local_abspath;
1047   svn_boolean_t use_url = svn_path_is_url(path_or_url);
1048   svn_client__pathrev_t *peg_loc;
1049
1050   if (ra_session && svn_path_is_url(path_or_url))
1051     {
1052       SVN_ERR(svn_ra_reparent(ra_session, path_or_url, scratch_pool));
1053       SVN_ERR(svn_client__resolve_rev_and_url(&peg_loc, ra_session,
1054                                               path_or_url,
1055                                               peg_revision,
1056                                               peg_revision,
1057                                               ctx, scratch_pool));
1058     }
1059   else
1060     {
1061       SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &peg_loc,
1062                                                 path_or_url, NULL,
1063                                                 peg_revision,
1064                                                 peg_revision, ctx, scratch_pool));
1065     }
1066
1067   /* If PATH_OR_URL is as working copy path determine if we will need to
1068      contact the repository for the requested PEG_REVISION. */
1069   if (!use_url)
1070     {
1071       svn_client__pathrev_t *origin;
1072       SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
1073                                       scratch_pool));
1074
1075       SVN_ERR(svn_client__wc_node_get_origin(&origin, local_abspath, ctx,
1076                                              scratch_pool, scratch_pool));
1077       if (!origin
1078           || strcmp(origin->url, peg_loc->url) != 0
1079           || peg_loc->rev != origin->rev)
1080       {
1081         use_url = TRUE; /* Don't rely on local mergeinfo */
1082       }
1083     }
1084
1085   SVN_ERR(svn_ra_get_repos_root2(ra_session, repos_root, result_pool));
1086
1087   if (use_url)
1088     {
1089       SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
1090         mergeinfo_catalog, ra_session, peg_loc->url, peg_loc->rev,
1091         svn_mergeinfo_inherited, FALSE, include_descendants,
1092         result_pool, scratch_pool));
1093     }
1094   else /* ! svn_path_is_url() */
1095     {
1096       SVN_ERR(svn_client__get_wc_or_repos_mergeinfo_catalog(
1097         mergeinfo_catalog, NULL, NULL, include_descendants, FALSE,
1098         ignore_invalid_mergeinfo, svn_mergeinfo_inherited,
1099         ra_session, path_or_url, ctx,
1100         result_pool, scratch_pool));
1101     }
1102
1103   return SVN_NO_ERROR;
1104 }
1105
1106 /*** In-memory mergeinfo elision ***/
1107 svn_error_t *
1108 svn_client__elide_mergeinfo_catalog(svn_mergeinfo_catalog_t mergeinfo_catalog,
1109                                     apr_pool_t *scratch_pool)
1110 {
1111   apr_array_header_t *sorted_hash;
1112   apr_array_header_t *elidable_paths = apr_array_make(scratch_pool, 1,
1113                                                       sizeof(const char *));
1114   apr_array_header_t *dir_stack = apr_array_make(scratch_pool, 1,
1115                                                  sizeof(const char *));
1116   apr_pool_t *iterpool;
1117   int i;
1118
1119   /* Here's the general algorithm:
1120      Walk through the paths sorted in tree order.  For each path, pop
1121      the dir_stack until it is either empty or the top item contains a parent
1122      of the current path. Check to see if that mergeinfo is then elidable,
1123      and build the list of elidable mergeinfo based upon that determination.
1124      Finally, push the path of interest onto the stack, and continue. */
1125   sorted_hash = svn_sort__hash(mergeinfo_catalog,
1126                                svn_sort_compare_items_as_paths,
1127                                scratch_pool);
1128   iterpool = svn_pool_create(scratch_pool);
1129   for (i = 0; i < sorted_hash->nelts; i++)
1130     {
1131       svn_sort__item_t *item = &APR_ARRAY_IDX(sorted_hash, i,
1132                                               svn_sort__item_t);
1133       const char *path = item->key;
1134
1135       if (dir_stack->nelts > 0)
1136         {
1137           const char *top;
1138           const char *path_suffix;
1139           svn_boolean_t elides = FALSE;
1140
1141           svn_pool_clear(iterpool);
1142
1143           /* Pop off any paths which are not ancestors of PATH. */
1144           do
1145             {
1146               top = APR_ARRAY_IDX(dir_stack, dir_stack->nelts - 1,
1147                                           const char *);
1148               path_suffix = svn_dirent_is_child(top, path, NULL);
1149
1150               if (!path_suffix)
1151                 apr_array_pop(dir_stack);
1152             }
1153           while (dir_stack->nelts > 0 && !path_suffix);
1154
1155           /* If we have a path suffix, it means we haven't popped the stack
1156              clean. */
1157           if (path_suffix)
1158             {
1159               SVN_ERR(should_elide_mergeinfo(&elides,
1160                                          svn_hash_gets(mergeinfo_catalog, top),
1161                                          svn_hash_gets(mergeinfo_catalog, path),
1162                                          path_suffix,
1163                                          iterpool));
1164
1165               if (elides)
1166                 APR_ARRAY_PUSH(elidable_paths, const char *) = path;
1167             }
1168         }
1169
1170       APR_ARRAY_PUSH(dir_stack, const char *) = path;
1171     }
1172   svn_pool_destroy(iterpool);
1173
1174   /* Now remove the elidable paths from the catalog. */
1175   for (i = 0; i < elidable_paths->nelts; i++)
1176     {
1177       const char *path = APR_ARRAY_IDX(elidable_paths, i, const char *);
1178       svn_hash_sets(mergeinfo_catalog, path, NULL);
1179     }
1180
1181   return SVN_NO_ERROR;
1182 }
1183
1184
1185 /* Helper for filter_log_entry_with_rangelist().
1186
1187    DEPTH_FIRST_CATALOG_INDEX is an array of svn_sort__item_t's.  The keys are
1188    repository-absolute const char *paths, the values are svn_mergeinfo_t for
1189    each path.
1190
1191    Return a pointer to the mergeinfo value of the nearest path-wise ancestor
1192    of FSPATH in DEPTH_FIRST_CATALOG_INDEX.  A path is considered its
1193    own ancestor, so if a key exactly matches FSPATH, return that
1194    key's mergeinfo and set *ANCESTOR_IS_SELF to true (set it to false in all
1195    other cases).
1196
1197    If DEPTH_FIRST_CATALOG_INDEX is NULL, empty, or no ancestor is found, then
1198    return NULL. */
1199 static svn_mergeinfo_t
1200 find_nearest_ancestor(const apr_array_header_t *depth_first_catalog_index,
1201                       svn_boolean_t *ancestor_is_self,
1202                       const char *fspath)
1203 {
1204   int ancestor_index = -1;
1205
1206   *ancestor_is_self = FALSE;
1207
1208   if (depth_first_catalog_index)
1209     {
1210       int i;
1211
1212       for (i = 0; i < depth_first_catalog_index->nelts; i++)
1213         {
1214           svn_sort__item_t item = APR_ARRAY_IDX(depth_first_catalog_index, i,
1215                                                 svn_sort__item_t);
1216           if (svn_fspath__skip_ancestor(item.key, fspath))
1217             {
1218               ancestor_index = i;
1219
1220               /* There's no nearer ancestor than FSPATH itself. */
1221               if (strcmp(item.key, fspath) == 0)
1222                 {
1223                   *ancestor_is_self = TRUE;
1224                   break;
1225                 }
1226             }
1227
1228         }
1229     }
1230
1231   if (ancestor_index == -1)
1232     return NULL;
1233   else
1234     return (APR_ARRAY_IDX(depth_first_catalog_index,
1235                           ancestor_index,
1236                           svn_sort__item_t)).value;
1237 }
1238
1239 /* Baton for use with the filter_log_entry_with_rangelist()
1240    svn_log_entry_receiver_t callback. */
1241 struct filter_log_entry_baton_t
1242 {
1243   /* Is TRUE if RANGELIST describes potentially merged revisions, is FALSE
1244      if RANGELIST describes potentially eligible revisions. */
1245   svn_boolean_t filtering_merged;
1246
1247   /* Unsorted array of repository relative paths representing the merge
1248      sources.  There will be more than one source  */
1249   const apr_array_header_t *merge_source_fspaths;
1250
1251   /* The repository-absolute path we are calling svn_client_log5() on. */
1252   const char *target_fspath;
1253
1254   /* Mergeinfo catalog for the tree rooted at TARGET_FSPATH.
1255      The path keys must be repository-absolute. */
1256   svn_mergeinfo_catalog_t target_mergeinfo_catalog;
1257
1258   /* Depth first sorted array of svn_sort__item_t's for
1259      TARGET_MERGEINFO_CATALOG. */
1260   apr_array_header_t *depth_first_catalog_index;
1261
1262   /* A rangelist describing all the revisions potentially merged or
1263      potentially eligible for merging (see FILTERING_MERGED) based on
1264      the target's explicit or inherited mergeinfo. */
1265   const svn_rangelist_t *rangelist;
1266
1267   /* The wrapped svn_log_entry_receiver_t callback and baton which
1268      filter_log_entry_with_rangelist() is acting as a filter for. */
1269   svn_log_entry_receiver_t log_receiver;
1270   void *log_receiver_baton;
1271
1272   svn_client_ctx_t *ctx;
1273 };
1274
1275 /* Implements the svn_log_entry_receiver_t interface.  BATON is a
1276    `struct filter_log_entry_baton_t *'.
1277
1278    Call the wrapped log receiver BATON->log_receiver (with
1279    BATON->log_receiver_baton) if:
1280
1281    BATON->FILTERING_MERGED is FALSE and the changes represented by LOG_ENTRY
1282    have been fully merged from BATON->merge_source_fspaths to the WC target
1283    based on the mergeinfo for the WC contained in BATON->TARGET_MERGEINFO_CATALOG.
1284
1285    Or
1286
1287    BATON->FILTERING_MERGED is TRUE and the changes represented by LOG_ENTRY
1288    have not been merged, or only partially merged, from
1289    BATON->merge_source_fspaths to the WC target based on the mergeinfo for the
1290    WC contained in BATON->TARGET_MERGEINFO_CATALOG. */
1291 static svn_error_t *
1292 filter_log_entry_with_rangelist(void *baton,
1293                                 svn_log_entry_t *log_entry,
1294                                 apr_pool_t *pool)
1295 {
1296   struct filter_log_entry_baton_t *fleb = baton;
1297   svn_rangelist_t *intersection, *this_rangelist;
1298
1299   if (fleb->ctx->cancel_func)
1300     SVN_ERR(fleb->ctx->cancel_func(fleb->ctx->cancel_baton));
1301
1302   /* Ignore r0 because there can be no "change 0" in a merge range. */
1303   if (log_entry->revision == 0)
1304     return SVN_NO_ERROR;
1305
1306   this_rangelist = svn_rangelist__initialize(log_entry->revision - 1,
1307                                              log_entry->revision,
1308                                              TRUE, pool);
1309
1310   /* Don't consider inheritance yet, see if LOG_ENTRY->REVISION is
1311      fully or partially represented in BATON->RANGELIST. */
1312   SVN_ERR(svn_rangelist_intersect(&intersection, fleb->rangelist,
1313                                   this_rangelist, FALSE, pool));
1314   if (! (intersection && intersection->nelts))
1315     return SVN_NO_ERROR;
1316
1317   SVN_ERR_ASSERT(intersection->nelts == 1);
1318
1319   /* Ok, we know LOG_ENTRY->REVISION is represented in BATON->RANGELIST,
1320      but is it only partially represented, i.e. is the corresponding range in
1321      BATON->RANGELIST non-inheritable?  Ask for the same intersection as
1322      above but consider inheritance this time, if the intersection is empty
1323      we know the range in BATON->RANGELIST is non-inheritable. */
1324   SVN_ERR(svn_rangelist_intersect(&intersection, fleb->rangelist,
1325                                   this_rangelist, TRUE, pool));
1326   log_entry->non_inheritable = !intersection->nelts;
1327
1328   /* If the paths changed by LOG_ENTRY->REVISION are provided we can determine
1329      if LOG_ENTRY->REVISION, while only partially represented in
1330      BATON->RANGELIST, is in fact completely applied to all affected paths.
1331      ### And ... what if it is, or if it isn't? What do we do with the answer?
1332          And how do we cope if the changed paths are not provided? */
1333   if ((log_entry->non_inheritable || !fleb->filtering_merged)
1334       && log_entry->changed_paths2)
1335     {
1336       apr_hash_index_t *hi;
1337       svn_boolean_t all_subtrees_have_this_rev = TRUE;
1338       svn_rangelist_t *this_rev_rangelist =
1339         svn_rangelist__initialize(log_entry->revision - 1,
1340                                   log_entry->revision, TRUE, pool);
1341       apr_pool_t *iterpool = svn_pool_create(pool);
1342
1343       for (hi = apr_hash_first(pool, log_entry->changed_paths2);
1344            hi;
1345            hi = apr_hash_next(hi))
1346         {
1347           int i;
1348           const char *path = svn__apr_hash_index_key(hi);
1349           svn_log_changed_path2_t *change = svn__apr_hash_index_val(hi);
1350           const char *target_fspath_affected;
1351           svn_mergeinfo_t nearest_ancestor_mergeinfo;
1352           svn_boolean_t found_this_revision = FALSE;
1353           const char *merge_source_rel_target;
1354           const char *merge_source_fspath;
1355           svn_boolean_t ancestor_is_self;
1356
1357           svn_pool_clear(iterpool);
1358
1359           /* Check that PATH is a subtree of at least one of the
1360              merge sources.  If not then ignore this path.  */
1361           for (i = 0; i < fleb->merge_source_fspaths->nelts; i++)
1362             {
1363               merge_source_fspath = APR_ARRAY_IDX(fleb->merge_source_fspaths,
1364                                                   i, const char *);
1365
1366               merge_source_rel_target
1367                 = svn_fspath__skip_ancestor(merge_source_fspath, path);
1368               if (merge_source_rel_target)
1369                 {
1370                   /* If MERGE_SOURCE was itself deleted, replaced, or added
1371                      in LOG_ENTRY->REVISION then ignore this PATH since you
1372                      can't merge a addition or deletion of yourself. */
1373                   if (merge_source_rel_target[0] == '\0'
1374                       && (change->action != 'M'))
1375                     i = fleb->merge_source_fspaths->nelts;
1376                   break;
1377                 }
1378             }
1379           /* If we examined every merge source path and PATH is a child of
1380              none of them then we can ignore this PATH. */
1381           if (i == fleb->merge_source_fspaths->nelts)
1382             continue;
1383
1384           /* Calculate the target path which PATH would affect if merged. */
1385           target_fspath_affected = svn_fspath__join(fleb->target_fspath,
1386                                                     merge_source_rel_target,
1387                                                     iterpool);
1388
1389           nearest_ancestor_mergeinfo =
1390             find_nearest_ancestor(fleb->depth_first_catalog_index,
1391                                   &ancestor_is_self,
1392                                   target_fspath_affected);
1393
1394           /* Issue #3791: A path should never have explicit mergeinfo
1395              describing its own addition (that's self-referential).  Nor will
1396              it have explicit mergeinfo describing its own deletion (we
1397              obviously can't add new mergeinfo to a path we are deleting).
1398
1399              This lack of explicit mergeinfo should not cause such revisions
1400              to show up as eligible however.  If PATH was deleted, replaced,
1401              or added in LOG_ENTRY->REVISION, but the corresponding
1402              TARGET_PATH_AFFECTED already exists and has explicit mergeinfo
1403              describing merges from PATH *after* LOG_ENTRY->REVISION, then
1404              ignore this PATH.  If it was deleted in LOG_ENTRY->REVISION it's
1405              obviously back.  If it was added or replaced it's still around
1406              possibly it was replaced one or more times, but it's back now.
1407              Regardless, LOG_ENTRY->REVISION is *not* an eligible revision! */
1408           if (nearest_ancestor_mergeinfo &&
1409               ancestor_is_self /* Explicit mergeinfo on TARGET_PATH_AFFECTED */
1410               && (change->action != 'M'))
1411             {
1412               svn_rangelist_t *rangelist =
1413                   svn_hash_gets(nearest_ancestor_mergeinfo, path);
1414               if (rangelist)
1415                 {
1416                   svn_merge_range_t *youngest_range = APR_ARRAY_IDX(
1417                     rangelist, rangelist->nelts - 1, svn_merge_range_t *);
1418
1419                   if (youngest_range
1420                       && (youngest_range->end > log_entry->revision))
1421                     continue;
1422                 }
1423             }
1424
1425           if (nearest_ancestor_mergeinfo)
1426             {
1427               apr_hash_index_t *hi2;
1428
1429               for (hi2 = apr_hash_first(iterpool, nearest_ancestor_mergeinfo);
1430                    hi2;
1431                    hi2 = apr_hash_next(hi2))
1432                 {
1433                   const char *mergeinfo_path = svn__apr_hash_index_key(hi2);
1434                   svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi2);
1435
1436                   /* Does the mergeinfo for PATH reflect if
1437                      LOG_ENTRY->REVISION was previously merged
1438                      from MERGE_SOURCE_FSPATH? */
1439                   if (svn_fspath__skip_ancestor(merge_source_fspath,
1440                                                 mergeinfo_path))
1441                     {
1442                       /* Something was merged from MERGE_SOURCE_FSPATH, does
1443                          it include LOG_ENTRY->REVISION? */
1444                       SVN_ERR(svn_rangelist_intersect(&intersection,
1445                                                       rangelist,
1446                                                       this_rev_rangelist,
1447                                                       FALSE,
1448                                                       iterpool));
1449                       if (intersection->nelts)
1450                         {
1451                           if (ancestor_is_self)
1452                             {
1453                               /* TARGET_PATH_AFFECTED has explicit mergeinfo,
1454                                  so we don't need to worry if that mergeinfo
1455                                  is inheritable or not. */
1456                               found_this_revision = TRUE;
1457                               break;
1458                             }
1459                           else
1460                             {
1461                               /* TARGET_PATH_AFFECTED inherited its mergeinfo,
1462                                  so we have to ignore non-inheritable
1463                                  ranges. */
1464                               SVN_ERR(svn_rangelist_intersect(
1465                                 &intersection,
1466                                 rangelist,
1467                                 this_rev_rangelist,
1468                                 TRUE, iterpool));
1469                               if (intersection->nelts)
1470                                 {
1471                                   found_this_revision = TRUE;
1472                                   break;
1473                                 }
1474                             }
1475                         }
1476                     }
1477                 }
1478             }
1479
1480           if (!found_this_revision)
1481             {
1482               /* As soon as any PATH is found that is not fully merged for
1483                  LOG_ENTRY->REVISION then we can stop. */
1484               all_subtrees_have_this_rev = FALSE;
1485               break;
1486             }
1487         }
1488
1489       svn_pool_destroy(iterpool);
1490
1491       if (all_subtrees_have_this_rev)
1492         {
1493           if (fleb->filtering_merged)
1494             log_entry->non_inheritable = FALSE;
1495           else
1496             return SVN_NO_ERROR;
1497         }
1498     }
1499
1500   /* Call the wrapped log receiver which this function is filtering for. */
1501   return fleb->log_receiver(fleb->log_receiver_baton, log_entry, pool);
1502 }
1503
1504 static svn_error_t *
1505 logs_for_mergeinfo_rangelist(const char *source_url,
1506                              const apr_array_header_t *merge_source_fspaths,
1507                              svn_boolean_t filtering_merged,
1508                              const svn_rangelist_t *rangelist,
1509                              svn_boolean_t oldest_revs_first,
1510                              svn_mergeinfo_catalog_t target_mergeinfo_catalog,
1511                              const char *target_fspath,
1512                              svn_boolean_t discover_changed_paths,
1513                              const apr_array_header_t *revprops,
1514                              svn_log_entry_receiver_t log_receiver,
1515                              void *log_receiver_baton,
1516                              svn_client_ctx_t *ctx,
1517                              svn_ra_session_t *ra_session,
1518                              apr_pool_t *scratch_pool)
1519 {
1520   svn_merge_range_t *oldest_range, *youngest_range;
1521   svn_revnum_t oldest_rev, youngest_rev;
1522   struct filter_log_entry_baton_t fleb;
1523
1524   if (! rangelist->nelts)
1525     return SVN_NO_ERROR;
1526
1527   /* Calculate and construct the bounds of our log request. */
1528   youngest_range = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
1529                                  svn_merge_range_t *);
1530   youngest_rev = youngest_range->end;
1531   oldest_range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
1532   oldest_rev = oldest_range->start;
1533
1534   if (! target_mergeinfo_catalog)
1535     target_mergeinfo_catalog = apr_hash_make(scratch_pool);
1536
1537   /* FILTER_LOG_ENTRY_BATON_T->TARGET_MERGEINFO_CATALOG's keys are required
1538      to be repository-absolute. */
1539   SVN_ERR(svn_mergeinfo__add_prefix_to_catalog(&target_mergeinfo_catalog,
1540                                                target_mergeinfo_catalog, "/",
1541                                                scratch_pool, scratch_pool));
1542
1543   /* Build the log filtering callback baton. */
1544   fleb.filtering_merged = filtering_merged;
1545   fleb.merge_source_fspaths = merge_source_fspaths;
1546   fleb.target_mergeinfo_catalog = target_mergeinfo_catalog;
1547   fleb.depth_first_catalog_index =
1548     svn_sort__hash(target_mergeinfo_catalog,
1549                    svn_sort_compare_items_as_paths,
1550                    scratch_pool);
1551   fleb.target_fspath = target_fspath;
1552   fleb.rangelist = rangelist;
1553   fleb.log_receiver = log_receiver;
1554   fleb.log_receiver_baton = log_receiver_baton;
1555   fleb.ctx = ctx;
1556
1557   if (!ra_session)
1558     SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, source_url,
1559                                                  NULL, NULL, FALSE, FALSE, ctx,
1560                                                  scratch_pool, scratch_pool));
1561   else
1562     SVN_ERR(svn_ra_reparent(ra_session, source_url, scratch_pool));
1563
1564   {
1565     apr_array_header_t *target;
1566     target = apr_array_make(scratch_pool, 1, sizeof(const char *));
1567     APR_ARRAY_PUSH(target, const char *) = "";
1568
1569     SVN_ERR(svn_ra_get_log2(ra_session, target,
1570                             oldest_revs_first ? oldest_rev : youngest_rev,
1571                             oldest_revs_first ? youngest_rev : oldest_rev,
1572                             0 /* limit */,
1573                             discover_changed_paths,
1574                             FALSE /* strict_node_history */,
1575                             FALSE /* include_merged_revisions */,
1576                             revprops,
1577                             filter_log_entry_with_rangelist, &fleb,
1578                             scratch_pool));
1579   }
1580
1581   /* Check for cancellation. */
1582   if (ctx->cancel_func)
1583     SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
1584
1585   return SVN_NO_ERROR;
1586 }
1587
1588 /* Set *OUT_MERGEINFO to a shallow copy of MERGEINFO with each source path
1589    converted to a (URI-encoded) URL based on REPOS_ROOT_URL. *OUT_MERGEINFO
1590    is declared as 'apr_hash_t *' because its key do not obey the rules of
1591    'svn_mergeinfo_t'.
1592
1593    Allocate *OUT_MERGEINFO and the new keys in RESULT_POOL.  Use
1594    SCRATCH_POOL for any temporary allocations. */
1595 static svn_error_t *
1596 mergeinfo_relpaths_to_urls(apr_hash_t **out_mergeinfo,
1597                            svn_mergeinfo_t mergeinfo,
1598                            const char *repos_root_url,
1599                            apr_pool_t *result_pool,
1600                            apr_pool_t *scratch_pool)
1601 {
1602   *out_mergeinfo = NULL;
1603   if (mergeinfo)
1604     {
1605       apr_hash_index_t *hi;
1606       apr_hash_t *full_path_mergeinfo = apr_hash_make(result_pool);
1607
1608       for (hi = apr_hash_first(scratch_pool, mergeinfo);
1609            hi; hi = apr_hash_next(hi))
1610         {
1611           const char *key = svn__apr_hash_index_key(hi);
1612           void *val = svn__apr_hash_index_val(hi);
1613
1614           svn_hash_sets(full_path_mergeinfo,
1615                         svn_path_url_add_component2(repos_root_url, key + 1,
1616                                                     result_pool),
1617                         val);
1618         }
1619       *out_mergeinfo = full_path_mergeinfo;
1620     }
1621
1622   return SVN_NO_ERROR;
1623 }
1624
1625 \f
1626 /*** Public APIs ***/
1627
1628 svn_error_t *
1629 svn_client_mergeinfo_get_merged(apr_hash_t **mergeinfo_p,
1630                                 const char *path_or_url,
1631                                 const svn_opt_revision_t *peg_revision,
1632                                 svn_client_ctx_t *ctx,
1633                                 apr_pool_t *pool)
1634 {
1635   const char *repos_root;
1636   svn_mergeinfo_catalog_t mergeinfo_cat;
1637   svn_mergeinfo_t mergeinfo;
1638
1639   SVN_ERR(get_mergeinfo(&mergeinfo_cat, &repos_root, path_or_url,
1640                         peg_revision, FALSE, FALSE, ctx, NULL, pool, pool));
1641   if (mergeinfo_cat)
1642     {
1643       const char *repos_relpath;
1644
1645       if (! svn_path_is_url(path_or_url))
1646         {
1647           SVN_ERR(svn_dirent_get_absolute(&path_or_url, path_or_url, pool));
1648           SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, NULL, NULL,
1649                                               ctx->wc_ctx, path_or_url,
1650                                               pool, pool));
1651         }
1652       else
1653         {
1654           repos_relpath = svn_uri_skip_ancestor(repos_root, path_or_url, pool);
1655
1656           SVN_ERR_ASSERT(repos_relpath != NULL); /* Or get_mergeinfo failed */
1657         }
1658
1659       mergeinfo = svn_hash_gets(mergeinfo_cat, repos_relpath);
1660     }
1661   else
1662     {
1663       mergeinfo = NULL;
1664     }
1665
1666   SVN_ERR(mergeinfo_relpaths_to_urls(mergeinfo_p, mergeinfo,
1667                                      repos_root, pool, pool));
1668   return SVN_NO_ERROR;
1669 }
1670
1671 svn_error_t *
1672 svn_client__mergeinfo_log(svn_boolean_t finding_merged,
1673                           const char *target_path_or_url,
1674                           const svn_opt_revision_t *target_peg_revision,
1675                           svn_mergeinfo_catalog_t *target_mergeinfo_catalog,
1676                           const char *source_path_or_url,
1677                           const svn_opt_revision_t *source_peg_revision,
1678                           const svn_opt_revision_t *source_start_revision,
1679                           const svn_opt_revision_t *source_end_revision,
1680                           svn_log_entry_receiver_t log_receiver,
1681                           void *log_receiver_baton,
1682                           svn_boolean_t discover_changed_paths,
1683                           svn_depth_t depth,
1684                           const apr_array_header_t *revprops,
1685                           svn_client_ctx_t *ctx,
1686                           svn_ra_session_t *ra_session,
1687                           apr_pool_t *result_pool,
1688                           apr_pool_t *scratch_pool)
1689 {
1690   const char *log_target = NULL;
1691   const char *repos_root;
1692   const char *target_repos_relpath;
1693   svn_mergeinfo_catalog_t target_mergeinfo_cat;
1694   svn_ra_session_t *target_session = NULL;
1695   svn_client__pathrev_t *pathrev;
1696
1697   /* A hash of paths, at or under TARGET_PATH_OR_URL, mapped to
1698      rangelists.  Not technically mergeinfo, so not using the
1699      svn_mergeinfo_t type. */
1700   apr_hash_t *inheritable_subtree_merges;
1701
1702   svn_mergeinfo_t source_history;
1703   svn_mergeinfo_t target_history;
1704   svn_rangelist_t *master_noninheritable_rangelist;
1705   svn_rangelist_t *master_inheritable_rangelist;
1706   apr_array_header_t *merge_source_fspaths =
1707     apr_array_make(scratch_pool, 1, sizeof(const char *));
1708   apr_hash_index_t *hi_catalog;
1709   apr_hash_index_t *hi;
1710   apr_pool_t *iterpool;
1711   svn_boolean_t oldest_revs_first = TRUE;
1712   apr_pool_t *subpool;
1713
1714   /* We currently only support depth = empty | infinity. */
1715   if (depth != svn_depth_infinity && depth != svn_depth_empty)
1716     return svn_error_create(
1717       SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1718       _("Only depths 'infinity' and 'empty' are currently supported"));
1719
1720   /* Validate and sanitize the incoming source operative revision range. */
1721   if (!((source_start_revision->kind == svn_opt_revision_unspecified) ||
1722         (source_start_revision->kind == svn_opt_revision_number) ||
1723         (source_start_revision->kind == svn_opt_revision_date) ||
1724         (source_start_revision->kind == svn_opt_revision_head)))
1725     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1726   if (!((source_end_revision->kind == svn_opt_revision_unspecified) ||
1727         (source_end_revision->kind == svn_opt_revision_number) ||
1728         (source_end_revision->kind == svn_opt_revision_date) ||
1729         (source_end_revision->kind == svn_opt_revision_head)))
1730     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1731   if ((source_end_revision->kind != svn_opt_revision_unspecified)
1732       && (source_start_revision->kind == svn_opt_revision_unspecified))
1733     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1734   if ((source_end_revision->kind == svn_opt_revision_unspecified)
1735       && (source_start_revision->kind != svn_opt_revision_unspecified))
1736     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1737
1738   subpool = svn_pool_create(scratch_pool);
1739
1740   if (ra_session)
1741     target_session = ra_session;
1742
1743   /* We need the union of TARGET_PATH_OR_URL@TARGET_PEG_REVISION's mergeinfo
1744      and MERGE_SOURCE_URL's history.  It's not enough to do path
1745      matching, because renames in the history of MERGE_SOURCE_URL
1746      throw that all in a tizzy.  Of course, if there's no mergeinfo on
1747      the target, that vastly simplifies matters (we'll have nothing to
1748      do). */
1749   /* This get_mergeinfo() call doubles as a mergeinfo capabilities check. */
1750   if (target_mergeinfo_catalog)
1751     {
1752       if (*target_mergeinfo_catalog)
1753         {
1754           /* The caller provided the mergeinfo catalog for
1755              TARGET_PATH_OR_URL, so we don't need to accquire
1756              it ourselves.  We do need to get the repos_root
1757              though, because get_mergeinfo() won't do it for us. */
1758           target_mergeinfo_cat = *target_mergeinfo_catalog;
1759
1760           if (ra_session && svn_path_is_url(target_path_or_url))
1761             {
1762               SVN_ERR(svn_ra_reparent(ra_session, target_path_or_url, subpool));
1763               SVN_ERR(svn_client__resolve_rev_and_url(&pathrev, ra_session,
1764                                                       target_path_or_url,
1765                                                       target_peg_revision,
1766                                                       target_peg_revision,
1767                                                       ctx, subpool));
1768               target_session = ra_session;
1769             }
1770           else
1771             {
1772               SVN_ERR(svn_client__ra_session_from_path2(&target_session,
1773                                                         &pathrev,
1774                                                         target_path_or_url,
1775                                                         NULL,
1776                                                         target_peg_revision,
1777                                                         target_peg_revision,
1778                                                         ctx, subpool));
1779             }
1780           SVN_ERR(svn_ra_get_repos_root2(target_session, &repos_root,
1781                                          scratch_pool));
1782         }
1783       else
1784         {
1785           /* The caller didn't provide the mergeinfo catalog for
1786              TARGET_PATH_OR_URL, but wants us to pass a copy back
1787              when we get it, so use RESULT_POOL. */
1788           SVN_ERR(get_mergeinfo(target_mergeinfo_catalog, &repos_root,
1789                                 target_path_or_url, target_peg_revision,
1790                                 depth == svn_depth_infinity, TRUE,
1791                                 ctx, ra_session, result_pool, scratch_pool));
1792           target_mergeinfo_cat = *target_mergeinfo_catalog;
1793         }
1794     }
1795   else
1796     {
1797       /* The caller didn't provide the mergeinfo catalog for
1798          TARGET_PATH_OR_URL, nor does it want a copy, so we can use
1799          nothing but SCRATCH_POOL. */
1800       SVN_ERR(get_mergeinfo(&target_mergeinfo_cat, &repos_root,
1801                             target_path_or_url, target_peg_revision,
1802                             depth == svn_depth_infinity, TRUE,
1803                             ctx, ra_session, scratch_pool, scratch_pool));
1804     }
1805
1806   if (!svn_path_is_url(target_path_or_url))
1807     {
1808       SVN_ERR(svn_dirent_get_absolute(&target_path_or_url,
1809                                       target_path_or_url, scratch_pool));
1810       SVN_ERR(svn_wc__node_get_repos_info(NULL, &target_repos_relpath,
1811                                           NULL, NULL,
1812                                           ctx->wc_ctx, target_path_or_url,
1813                                           scratch_pool, scratch_pool));
1814     }
1815   else
1816     {
1817       target_repos_relpath = svn_uri_skip_ancestor(repos_root,
1818                                                    target_path_or_url,
1819                                                    scratch_pool);
1820
1821       /* TARGET_REPOS_REL should be non-NULL, else get_mergeinfo
1822          should have failed.  */
1823       SVN_ERR_ASSERT(target_repos_relpath != NULL);
1824     }
1825
1826   if (!target_mergeinfo_cat)
1827     {
1828       /* If we are looking for what has been merged and there is no
1829          mergeinfo then we already know the answer.  If we are looking
1830          for eligible revisions then create a catalog with empty mergeinfo
1831          on the target.  This is semantically equivalent to no mergeinfo
1832          and gives us something to combine with MERGE_SOURCE_URL's
1833          history. */
1834       if (finding_merged)
1835         {
1836           svn_pool_destroy(subpool);
1837           return SVN_NO_ERROR;
1838         }
1839       else
1840         {
1841           target_mergeinfo_cat = apr_hash_make(scratch_pool);
1842           svn_hash_sets(target_mergeinfo_cat, target_repos_relpath,
1843                         apr_hash_make(scratch_pool));
1844         }
1845     }
1846
1847   /* Fetch the location history as mergeinfo, for the source branch
1848    * (between the given start and end revisions), and, if we're finding
1849    * merged revisions, then also for the entire target branch.
1850    *
1851    * ### TODO: As the source and target must be in the same repository, we
1852    * should share a single session, tracking the two URLs separately. */
1853   {
1854     svn_ra_session_t *source_session;
1855     svn_revnum_t start_rev, end_rev, youngest_rev = SVN_INVALID_REVNUM;
1856
1857     if (! finding_merged)
1858       {
1859         if (!target_session)
1860           SVN_ERR(svn_client__ra_session_from_path2(&target_session, &pathrev,
1861                                                     target_path_or_url, NULL,
1862                                                     target_peg_revision,
1863                                                     target_peg_revision,
1864                                                     ctx, subpool));
1865         SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history, NULL,
1866                                                      pathrev,
1867                                                      SVN_INVALID_REVNUM,
1868                                                      SVN_INVALID_REVNUM,
1869                                                      target_session, ctx,
1870                                                      scratch_pool));
1871       }
1872
1873     if (target_session
1874         && svn_path_is_url(source_path_or_url)
1875         && repos_root
1876         && svn_uri_skip_ancestor(repos_root, source_path_or_url, subpool))
1877       {
1878         /* We can re-use the existing session */
1879         source_session = target_session;
1880         SVN_ERR(svn_ra_reparent(source_session, source_path_or_url, subpool));
1881         SVN_ERR(svn_client__resolve_rev_and_url(&pathrev, source_session,
1882                                                 source_path_or_url,
1883                                                 source_peg_revision,
1884                                                 source_peg_revision,
1885                                                 ctx, subpool));
1886       }
1887     else
1888       {
1889         SVN_ERR(svn_client__ra_session_from_path2(&source_session, &pathrev,
1890                                                   source_path_or_url, NULL,
1891                                                   source_peg_revision,
1892                                                   source_peg_revision,
1893                                                   ctx, subpool));
1894       }
1895     SVN_ERR(svn_client__get_revision_number(&start_rev, &youngest_rev,
1896                                             ctx->wc_ctx, source_path_or_url,
1897                                             source_session,
1898                                             source_start_revision,
1899                                             subpool));
1900     SVN_ERR(svn_client__get_revision_number(&end_rev, &youngest_rev,
1901                                             ctx->wc_ctx, source_path_or_url,
1902                                             source_session,
1903                                             source_end_revision,
1904                                             subpool));
1905     SVN_ERR(svn_client__get_history_as_mergeinfo(&source_history, NULL,
1906                                                  pathrev,
1907                                                  MAX(end_rev, start_rev),
1908                                                  MIN(end_rev, start_rev),
1909                                                  source_session, ctx,
1910                                                  scratch_pool));
1911     if (start_rev > end_rev)
1912       oldest_revs_first = FALSE;
1913   }
1914
1915   /* Separate the explicit or inherited mergeinfo on TARGET_PATH_OR_URL,
1916      and possibly its explicit subtree mergeinfo, into their
1917      inheritable and non-inheritable parts. */
1918   master_noninheritable_rangelist = apr_array_make(scratch_pool, 64,
1919                                                    sizeof(svn_merge_range_t *));
1920   master_inheritable_rangelist = apr_array_make(scratch_pool, 64,
1921                                                 sizeof(svn_merge_range_t *));
1922   inheritable_subtree_merges = apr_hash_make(scratch_pool);
1923
1924   iterpool = svn_pool_create(scratch_pool);
1925
1926   for (hi_catalog = apr_hash_first(scratch_pool, target_mergeinfo_cat);
1927        hi_catalog;
1928        hi_catalog = apr_hash_next(hi_catalog))
1929     {
1930       svn_mergeinfo_t subtree_mergeinfo = svn__apr_hash_index_val(hi_catalog);
1931       svn_mergeinfo_t subtree_history;
1932       svn_mergeinfo_t subtree_source_history;
1933       svn_mergeinfo_t subtree_inheritable_mergeinfo;
1934       svn_mergeinfo_t subtree_noninheritable_mergeinfo;
1935       svn_mergeinfo_t merged_noninheritable;
1936       svn_mergeinfo_t merged;
1937       const char *subtree_path = svn__apr_hash_index_key(hi_catalog);
1938       svn_boolean_t is_subtree = strcmp(subtree_path,
1939                                         target_repos_relpath) != 0;
1940       svn_pool_clear(iterpool);
1941
1942       if (is_subtree)
1943         {
1944           /* If SUBTREE_PATH is a proper subtree of TARGET_PATH_OR_URL
1945              then make a copy of SOURCE_HISTORY that is path adjusted
1946              for the subtree.  */
1947           const char *subtree_rel_path =
1948             subtree_path + strlen(target_repos_relpath) + 1;
1949
1950           SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
1951             &subtree_source_history, source_history,
1952             subtree_rel_path, scratch_pool, scratch_pool));
1953
1954           if (!finding_merged)
1955             SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
1956                     &subtree_history, target_history,
1957                     subtree_rel_path, scratch_pool, scratch_pool));
1958         }
1959       else
1960         {
1961           subtree_source_history = source_history;
1962           if (!finding_merged)
1963             subtree_history = target_history;
1964         }
1965
1966       if (!finding_merged)
1967         {
1968           svn_mergeinfo_t merged_via_history;
1969           SVN_ERR(svn_mergeinfo_intersect2(&merged_via_history,
1970                                            subtree_history,
1971                                            subtree_source_history, TRUE,
1972                                            scratch_pool, iterpool));
1973           SVN_ERR(svn_mergeinfo_merge2(subtree_mergeinfo,
1974                                        merged_via_history,
1975                                        scratch_pool, scratch_pool));
1976         }
1977
1978       SVN_ERR(svn_mergeinfo_inheritable2(&subtree_inheritable_mergeinfo,
1979                                          subtree_mergeinfo, NULL,
1980                                          SVN_INVALID_REVNUM,
1981                                          SVN_INVALID_REVNUM,
1982                                          TRUE, scratch_pool, iterpool));
1983       SVN_ERR(svn_mergeinfo_inheritable2(&subtree_noninheritable_mergeinfo,
1984                                          subtree_mergeinfo, NULL,
1985                                          SVN_INVALID_REVNUM,
1986                                          SVN_INVALID_REVNUM,
1987                                          FALSE, scratch_pool, iterpool));
1988
1989       /* Find the intersection of the non-inheritable part of
1990          SUBTREE_MERGEINFO and SOURCE_HISTORY.  svn_mergeinfo_intersect2()
1991          won't consider non-inheritable and inheritable ranges
1992          intersecting unless we ignore inheritance, but in doing so the
1993          resulting intersections have all inheritable ranges.  To get
1994          around this we set the inheritance on the result to all
1995          non-inheritable. */
1996       SVN_ERR(svn_mergeinfo_intersect2(&merged_noninheritable,
1997                                        subtree_noninheritable_mergeinfo,
1998                                        subtree_source_history, FALSE,
1999                                        scratch_pool, iterpool));
2000       svn_mergeinfo__set_inheritance(merged_noninheritable, FALSE,
2001                                      scratch_pool);
2002
2003       /* Keep track of all ranges partially merged to any and all
2004          subtrees. */
2005       SVN_ERR(svn_rangelist__merge_many(master_noninheritable_rangelist,
2006                                         merged_noninheritable,
2007                                         scratch_pool, iterpool));
2008
2009       /* Find the intersection of the inheritable part of TGT_MERGEINFO
2010          and SOURCE_HISTORY. */
2011       SVN_ERR(svn_mergeinfo_intersect2(&merged,
2012                                        subtree_inheritable_mergeinfo,
2013                                        subtree_source_history, FALSE,
2014                                        scratch_pool, iterpool));
2015
2016       /* Keep track of all ranges fully merged to any and all
2017          subtrees. */
2018       if (apr_hash_count(merged))
2019         {
2020           /* The inheritable rangelist merged from SUBTREE_SOURCE_HISTORY
2021              to SUBTREE_PATH. */
2022           svn_rangelist_t *subtree_merged_rangelist =
2023             apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
2024
2025           SVN_ERR(svn_rangelist__merge_many(master_inheritable_rangelist,
2026                                             merged, scratch_pool, iterpool));
2027           SVN_ERR(svn_rangelist__merge_many(subtree_merged_rangelist,
2028                                             merged, scratch_pool, iterpool));
2029
2030           svn_hash_sets(inheritable_subtree_merges, subtree_path,
2031                         subtree_merged_rangelist);
2032         }
2033       else
2034         {
2035           /* Map SUBTREE_PATH to an empty rangelist if there was nothing
2036              fully merged. e.g. Only empty or non-inheritable mergeinfo
2037              on the subtree or mergeinfo unrelated to the source. */
2038           svn_hash_sets(inheritable_subtree_merges, subtree_path,
2039                         apr_array_make(scratch_pool, 0,
2040                                        sizeof(svn_merge_range_t *)));
2041         }
2042     }
2043
2044   /* Make sure every range in MASTER_INHERITABLE_RANGELIST is fully merged to
2045      each subtree (including the target itself).  Any revisions which don't
2046      exist in *every* subtree are *potentially* only partially merged to the
2047      tree rooted at TARGET_PATH_OR_URL, so move those revisions to
2048      MASTER_NONINHERITABLE_RANGELIST.  It may turn out that that a revision
2049      was merged to the only subtree it affects, but we need to examine the
2050      logs to make this determination (which will be done by
2051      logs_for_mergeinfo_rangelist). */
2052   if (master_inheritable_rangelist->nelts)
2053     {
2054       for (hi = apr_hash_first(scratch_pool, inheritable_subtree_merges);
2055            hi;
2056            hi = apr_hash_next(hi))
2057         {
2058           svn_rangelist_t *deleted_rangelist;
2059           svn_rangelist_t *added_rangelist;
2060           svn_rangelist_t *subtree_merged_rangelist =
2061             svn__apr_hash_index_val(hi);
2062
2063           svn_pool_clear(iterpool);
2064
2065           SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
2066                                      master_inheritable_rangelist,
2067                                      subtree_merged_rangelist, TRUE,
2068                                      iterpool));
2069
2070           if (deleted_rangelist->nelts)
2071             {
2072               svn_rangelist__set_inheritance(deleted_rangelist, FALSE);
2073               SVN_ERR(svn_rangelist_merge2(master_noninheritable_rangelist,
2074                                            deleted_rangelist,
2075                                            scratch_pool, iterpool));
2076               SVN_ERR(svn_rangelist_remove(&master_inheritable_rangelist,
2077                                            deleted_rangelist,
2078                                            master_inheritable_rangelist,
2079                                            FALSE,
2080                                            scratch_pool));
2081             }
2082         }
2083     }
2084
2085   if (finding_merged)
2086     {
2087       /* Roll all the merged revisions into one rangelist. */
2088       SVN_ERR(svn_rangelist_merge2(master_inheritable_rangelist,
2089                                    master_noninheritable_rangelist,
2090                                    scratch_pool, scratch_pool));
2091
2092     }
2093   else
2094     {
2095       /* Create the starting rangelist for what might be eligible. */
2096       svn_rangelist_t *source_master_rangelist =
2097         apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
2098
2099       SVN_ERR(svn_rangelist__merge_many(source_master_rangelist,
2100                                         source_history,
2101                                         scratch_pool, scratch_pool));
2102
2103       /* From what might be eligible subtract what we know is
2104          partially merged and then merge that back. */
2105       SVN_ERR(svn_rangelist_remove(&source_master_rangelist,
2106                                    master_noninheritable_rangelist,
2107                                    source_master_rangelist,
2108                                    FALSE, scratch_pool));
2109       SVN_ERR(svn_rangelist_merge2(source_master_rangelist,
2110                                    master_noninheritable_rangelist,
2111                                    scratch_pool, scratch_pool));
2112       SVN_ERR(svn_rangelist_remove(&master_inheritable_rangelist,
2113                                    master_inheritable_rangelist,
2114                                    source_master_rangelist,
2115                                    TRUE, scratch_pool));
2116     }
2117
2118   /* Nothing merged?  Not even when considering shared history if
2119      looking for eligible revisions (i.e. !FINDING_MERGED)?  Then there
2120      is nothing more to do. */
2121   if (! master_inheritable_rangelist->nelts)
2122     {
2123       svn_pool_destroy(iterpool);
2124       return SVN_NO_ERROR;
2125     }
2126   else
2127     {
2128       /* Determine the correct (youngest) target for 'svn log'. */
2129       svn_merge_range_t *youngest_range
2130         = APR_ARRAY_IDX(master_inheritable_rangelist,
2131                         master_inheritable_rangelist->nelts - 1,
2132                         svn_merge_range_t *);
2133       svn_rangelist_t *youngest_rangelist =
2134         svn_rangelist__initialize(youngest_range->end - 1,
2135                                   youngest_range->end,
2136                                   youngest_range->inheritable,
2137                                   scratch_pool);;
2138
2139       for (hi = apr_hash_first(scratch_pool, source_history);
2140            hi;
2141            hi = apr_hash_next(hi))
2142         {
2143           const char *key = svn__apr_hash_index_key(hi);
2144           svn_rangelist_t *subtree_merged_rangelist =
2145             svn__apr_hash_index_val(hi);
2146           svn_rangelist_t *intersecting_rangelist;
2147
2148           svn_pool_clear(iterpool);
2149           SVN_ERR(svn_rangelist_intersect(&intersecting_rangelist,
2150                                           youngest_rangelist,
2151                                           subtree_merged_rangelist,
2152                                           FALSE, iterpool));
2153
2154           APR_ARRAY_PUSH(merge_source_fspaths, const char *) = key;
2155
2156           if (intersecting_rangelist->nelts)
2157             log_target = key;
2158         }
2159     }
2160
2161   svn_pool_destroy(iterpool);
2162
2163   /* Step 4: Finally, we run 'svn log' to drive our log receiver, but
2164      using a receiver filter to only allow revisions to pass through
2165      that are in our rangelist. */
2166   log_target = svn_path_url_add_component2(repos_root, log_target + 1,
2167                                            scratch_pool);
2168
2169   {
2170     svn_error_t *err;
2171
2172     err = logs_for_mergeinfo_rangelist(log_target, merge_source_fspaths,
2173                                        finding_merged,
2174                                        master_inheritable_rangelist,
2175                                        oldest_revs_first,
2176                                        target_mergeinfo_cat,
2177                                        svn_fspath__join("/",
2178                                                         target_repos_relpath,
2179                                                         scratch_pool),
2180                                        discover_changed_paths,
2181                                        revprops,
2182                                        log_receiver, log_receiver_baton,
2183                                        ctx, target_session, scratch_pool);
2184
2185     /* Close the source and target sessions. */
2186     svn_pool_destroy(subpool); /* For SVN_ERR_CEASE_INVOCATION */
2187
2188     return svn_error_trace(err);
2189   }
2190 }
2191
2192 svn_error_t *
2193 svn_client_mergeinfo_log2(svn_boolean_t finding_merged,
2194                           const char *target_path_or_url,
2195                           const svn_opt_revision_t *target_peg_revision,
2196                           const char *source_path_or_url,
2197                           const svn_opt_revision_t *source_peg_revision,
2198                           const svn_opt_revision_t *source_start_revision,
2199                           const svn_opt_revision_t *source_end_revision,
2200                           svn_log_entry_receiver_t log_receiver,
2201                           void *log_receiver_baton,
2202                           svn_boolean_t discover_changed_paths,
2203                           svn_depth_t depth,
2204                           const apr_array_header_t *revprops,
2205                           svn_client_ctx_t *ctx,
2206                           apr_pool_t *scratch_pool)
2207 {
2208   return svn_error_trace(
2209          svn_client__mergeinfo_log(finding_merged, target_path_or_url,
2210                                    target_peg_revision, NULL,
2211                                    source_path_or_url, source_peg_revision,
2212                                    source_start_revision, source_end_revision,
2213                                    log_receiver, log_receiver_baton,
2214                                    discover_changed_paths, depth, revprops,
2215                                    ctx, NULL,
2216                                    scratch_pool, scratch_pool));
2217 }
2218
2219 svn_error_t *
2220 svn_client_suggest_merge_sources(apr_array_header_t **suggestions,
2221                                  const char *path_or_url,
2222                                  const svn_opt_revision_t *peg_revision,
2223                                  svn_client_ctx_t *ctx,
2224                                  apr_pool_t *pool)
2225 {
2226   const char *repos_root;
2227   const char *copyfrom_path;
2228   apr_array_header_t *list;
2229   svn_revnum_t copyfrom_rev;
2230   svn_mergeinfo_catalog_t mergeinfo_cat;
2231   svn_mergeinfo_t mergeinfo;
2232   apr_hash_index_t *hi;
2233
2234   list = apr_array_make(pool, 1, sizeof(const char *));
2235
2236   /* In our ideal algorithm, the list of recommendations should be
2237      ordered by:
2238
2239         1. The most recent existing merge source.
2240         2. The copyfrom source (which will also be listed as a merge
2241            source if the copy was made with a 1.5+ client and server).
2242         3. All other merge sources, most recent to least recent.
2243
2244      However, determining the order of application of merge sources
2245      requires a new RA API.  Until such an API is available, our
2246      algorithm will be:
2247
2248         1. The copyfrom source.
2249         2. All remaining merge sources (unordered).
2250   */
2251
2252   /* ### TODO: Share ra_session batons to improve efficiency? */
2253   SVN_ERR(get_mergeinfo(&mergeinfo_cat, &repos_root, path_or_url,
2254                         peg_revision, FALSE, FALSE, ctx, NULL, pool, pool));
2255
2256   if (mergeinfo_cat && apr_hash_count(mergeinfo_cat))
2257     {
2258       /* We asked only for the PATH_OR_URL's mergeinfo, not any of its
2259          descendants.  So if there is anything in the catalog it is the
2260          mergeinfo for PATH_OR_URL. */
2261       mergeinfo = svn__apr_hash_index_val(apr_hash_first(pool, mergeinfo_cat));
2262     }
2263   else
2264     {
2265       mergeinfo = NULL;
2266     }
2267
2268   SVN_ERR(svn_client__get_copy_source(&copyfrom_path, &copyfrom_rev,
2269                                       path_or_url, peg_revision, ctx,
2270                                       pool, pool));
2271   if (copyfrom_path)
2272     {
2273       APR_ARRAY_PUSH(list, const char *) =
2274         svn_path_url_add_component2(repos_root, copyfrom_path, pool);
2275     }
2276
2277   if (mergeinfo)
2278     {
2279       for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2280         {
2281           const char *rel_path = svn__apr_hash_index_key(hi);
2282
2283           if (copyfrom_path == NULL || strcmp(rel_path, copyfrom_path) != 0)
2284             APR_ARRAY_PUSH(list, const char *) = \
2285               svn_path_url_add_component2(repos_root, rel_path + 1, pool);
2286         }
2287     }
2288
2289   *suggestions = list;
2290   return SVN_NO_ERROR;
2291 }
2292
2293 svn_error_t *
2294 svn_client__mergeinfo_status(svn_boolean_t *mergeinfo_changes,
2295                              svn_wc_context_t *wc_ctx,
2296                              const char *local_abspath,
2297                              apr_pool_t *scratch_pool)
2298 {
2299   apr_array_header_t *propchanges;
2300   int i;
2301
2302   *mergeinfo_changes = FALSE;
2303
2304   SVN_ERR(svn_wc_get_prop_diffs2(&propchanges, NULL, wc_ctx,
2305                                  local_abspath, scratch_pool, scratch_pool));
2306
2307   for (i = 0; i < propchanges->nelts; i++)
2308     {
2309       svn_prop_t prop = APR_ARRAY_IDX(propchanges, i, svn_prop_t);
2310       if (strcmp(prop.name, SVN_PROP_MERGEINFO) == 0)
2311         {
2312           *mergeinfo_changes = TRUE;
2313           break;
2314         }
2315     }
2316
2317   return SVN_NO_ERROR;
2318 }