]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_client/mergeinfo.c
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.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    Allocate *MERGEINFO_CATALOG and all its contents in RESULT_POOL.  Use
1025    SCRATCH_POOL for all temporary allocations.
1026
1027    Return SVN_ERR_UNSUPPORTED_FEATURE if the server does not support
1028    Merge Tracking.  */
1029 static svn_error_t *
1030 get_mergeinfo(svn_mergeinfo_catalog_t *mergeinfo_catalog,
1031               const char **repos_root,
1032               const char *path_or_url,
1033               const svn_opt_revision_t *peg_revision,
1034               svn_boolean_t include_descendants,
1035               svn_boolean_t ignore_invalid_mergeinfo,
1036               svn_client_ctx_t *ctx,
1037               apr_pool_t *result_pool,
1038               apr_pool_t *scratch_pool)
1039 {
1040   svn_ra_session_t *ra_session;
1041   const char *local_abspath;
1042   svn_boolean_t use_url = svn_path_is_url(path_or_url);
1043   svn_client__pathrev_t *peg_loc;
1044
1045   SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &peg_loc,
1046                                             path_or_url, NULL, peg_revision,
1047                                             peg_revision, ctx, scratch_pool));
1048
1049   /* If PATH_OR_URL is as working copy path determine if we will need to
1050      contact the repository for the requested PEG_REVISION. */
1051   if (!use_url)
1052     {
1053       svn_client__pathrev_t *origin;
1054       SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
1055                                       scratch_pool));
1056
1057       SVN_ERR(svn_client__wc_node_get_origin(&origin, local_abspath, ctx,
1058                                              scratch_pool, scratch_pool));
1059       if (!origin
1060           || strcmp(origin->url, peg_loc->url) != 0
1061           || peg_loc->rev != origin->rev)
1062       {
1063         use_url = TRUE; /* Don't rely on local mergeinfo */
1064       }
1065     }
1066
1067   /* Check server Merge Tracking capability. */
1068   SVN_ERR(svn_ra__assert_mergeinfo_capable_server(ra_session, path_or_url,
1069                                                   scratch_pool));
1070
1071   SVN_ERR(svn_ra_get_repos_root2(ra_session, repos_root, result_pool));
1072
1073   if (use_url)
1074     {
1075       SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
1076         mergeinfo_catalog, ra_session, peg_loc->url, peg_loc->rev,
1077         svn_mergeinfo_inherited, FALSE, include_descendants,
1078         result_pool, scratch_pool));
1079     }
1080   else /* ! svn_path_is_url() */
1081     {
1082       SVN_ERR(svn_client__get_wc_or_repos_mergeinfo_catalog(
1083         mergeinfo_catalog, NULL, NULL, include_descendants, FALSE,
1084         ignore_invalid_mergeinfo, svn_mergeinfo_inherited,
1085         ra_session, path_or_url, ctx,
1086         result_pool, scratch_pool));
1087     }
1088
1089   return SVN_NO_ERROR;
1090 }
1091
1092 /*** In-memory mergeinfo elision ***/
1093 svn_error_t *
1094 svn_client__elide_mergeinfo_catalog(svn_mergeinfo_catalog_t mergeinfo_catalog,
1095                                     apr_pool_t *scratch_pool)
1096 {
1097   apr_array_header_t *sorted_hash;
1098   apr_array_header_t *elidable_paths = apr_array_make(scratch_pool, 1,
1099                                                       sizeof(const char *));
1100   apr_array_header_t *dir_stack = apr_array_make(scratch_pool, 1,
1101                                                  sizeof(const char *));
1102   apr_pool_t *iterpool;
1103   int i;
1104
1105   /* Here's the general algorithm:
1106      Walk through the paths sorted in tree order.  For each path, pop
1107      the dir_stack until it is either empty or the top item contains a parent
1108      of the current path. Check to see if that mergeinfo is then elidable,
1109      and build the list of elidable mergeinfo based upon that determination.
1110      Finally, push the path of interest onto the stack, and continue. */
1111   sorted_hash = svn_sort__hash(mergeinfo_catalog,
1112                                svn_sort_compare_items_as_paths,
1113                                scratch_pool);
1114   iterpool = svn_pool_create(scratch_pool);
1115   for (i = 0; i < sorted_hash->nelts; i++)
1116     {
1117       svn_sort__item_t *item = &APR_ARRAY_IDX(sorted_hash, i,
1118                                               svn_sort__item_t);
1119       const char *path = item->key;
1120
1121       if (dir_stack->nelts > 0)
1122         {
1123           const char *top;
1124           const char *path_suffix;
1125           svn_boolean_t elides = FALSE;
1126
1127           svn_pool_clear(iterpool);
1128
1129           /* Pop off any paths which are not ancestors of PATH. */
1130           do
1131             {
1132               top = APR_ARRAY_IDX(dir_stack, dir_stack->nelts - 1,
1133                                           const char *);
1134               path_suffix = svn_dirent_is_child(top, path, NULL);
1135
1136               if (!path_suffix)
1137                 apr_array_pop(dir_stack);
1138             }
1139           while (dir_stack->nelts > 0 && !path_suffix);
1140
1141           /* If we have a path suffix, it means we haven't popped the stack
1142              clean. */
1143           if (path_suffix)
1144             {
1145               SVN_ERR(should_elide_mergeinfo(&elides,
1146                                          svn_hash_gets(mergeinfo_catalog, top),
1147                                          svn_hash_gets(mergeinfo_catalog, path),
1148                                          path_suffix,
1149                                          iterpool));
1150
1151               if (elides)
1152                 APR_ARRAY_PUSH(elidable_paths, const char *) = path;
1153             }
1154         }
1155
1156       APR_ARRAY_PUSH(dir_stack, const char *) = path;
1157     }
1158   svn_pool_destroy(iterpool);
1159
1160   /* Now remove the elidable paths from the catalog. */
1161   for (i = 0; i < elidable_paths->nelts; i++)
1162     {
1163       const char *path = APR_ARRAY_IDX(elidable_paths, i, const char *);
1164       svn_hash_sets(mergeinfo_catalog, path, NULL);
1165     }
1166
1167   return SVN_NO_ERROR;
1168 }
1169
1170
1171 /* Helper for filter_log_entry_with_rangelist().
1172
1173    DEPTH_FIRST_CATALOG_INDEX is an array of svn_sort__item_t's.  The keys are
1174    repository-absolute const char *paths, the values are svn_mergeinfo_t for
1175    each path.
1176
1177    Return a pointer to the mergeinfo value of the nearest path-wise ancestor
1178    of FSPATH in DEPTH_FIRST_CATALOG_INDEX.  A path is considered its
1179    own ancestor, so if a key exactly matches FSPATH, return that
1180    key's mergeinfo and set *ANCESTOR_IS_SELF to true (set it to false in all
1181    other cases).
1182
1183    If DEPTH_FIRST_CATALOG_INDEX is NULL, empty, or no ancestor is found, then
1184    return NULL. */
1185 static svn_mergeinfo_t
1186 find_nearest_ancestor(const apr_array_header_t *depth_first_catalog_index,
1187                       svn_boolean_t *ancestor_is_self,
1188                       const char *fspath)
1189 {
1190   int ancestor_index = -1;
1191
1192   *ancestor_is_self = FALSE;
1193
1194   if (depth_first_catalog_index)
1195     {
1196       int i;
1197
1198       for (i = 0; i < depth_first_catalog_index->nelts; i++)
1199         {
1200           svn_sort__item_t item = APR_ARRAY_IDX(depth_first_catalog_index, i,
1201                                                 svn_sort__item_t);
1202           if (svn_fspath__skip_ancestor(item.key, fspath))
1203             {
1204               ancestor_index = i;
1205
1206               /* There's no nearer ancestor than FSPATH itself. */
1207               if (strcmp(item.key, fspath) == 0)
1208                 {
1209                   *ancestor_is_self = TRUE;
1210                   break;
1211                 }
1212             }
1213
1214         }
1215     }
1216
1217   if (ancestor_index == -1)
1218     return NULL;
1219   else
1220     return (APR_ARRAY_IDX(depth_first_catalog_index,
1221                           ancestor_index,
1222                           svn_sort__item_t)).value;
1223 }
1224
1225 /* Baton for use with the filter_log_entry_with_rangelist()
1226    svn_log_entry_receiver_t callback. */
1227 struct filter_log_entry_baton_t
1228 {
1229   /* Is TRUE if RANGELIST describes potentially merged revisions, is FALSE
1230      if RANGELIST describes potentially eligible revisions. */
1231   svn_boolean_t filtering_merged;
1232
1233   /* Unsorted array of repository relative paths representing the merge
1234      sources.  There will be more than one source  */
1235   const apr_array_header_t *merge_source_fspaths;
1236
1237   /* The repository-absolute path we are calling svn_client_log5() on. */
1238   const char *target_fspath;
1239
1240   /* Mergeinfo catalog for the tree rooted at TARGET_FSPATH.
1241      The path keys must be repository-absolute. */
1242   svn_mergeinfo_catalog_t target_mergeinfo_catalog;
1243
1244   /* Depth first sorted array of svn_sort__item_t's for
1245      TARGET_MERGEINFO_CATALOG. */
1246   apr_array_header_t *depth_first_catalog_index;
1247
1248   /* A rangelist describing all the revisions potentially merged or
1249      potentially eligible for merging (see FILTERING_MERGED) based on
1250      the target's explicit or inherited mergeinfo. */
1251   const svn_rangelist_t *rangelist;
1252
1253   /* The wrapped svn_log_entry_receiver_t callback and baton which
1254      filter_log_entry_with_rangelist() is acting as a filter for. */
1255   svn_log_entry_receiver_t log_receiver;
1256   void *log_receiver_baton;
1257
1258   svn_client_ctx_t *ctx;
1259 };
1260
1261 /* Implements the svn_log_entry_receiver_t interface.  BATON is a
1262    `struct filter_log_entry_baton_t *'.
1263
1264    Call the wrapped log receiver BATON->log_receiver (with
1265    BATON->log_receiver_baton) if:
1266
1267    BATON->FILTERING_MERGED is FALSE and the changes represented by LOG_ENTRY
1268    have been fully merged from BATON->merge_source_fspaths to the WC target
1269    based on the mergeinfo for the WC contained in BATON->TARGET_MERGEINFO_CATALOG.
1270
1271    Or
1272
1273    BATON->FILTERING_MERGED is TRUE and the changes represented by LOG_ENTRY
1274    have not been merged, or only partially merged, from
1275    BATON->merge_source_fspaths to the WC target based on the mergeinfo for the
1276    WC contained in BATON->TARGET_MERGEINFO_CATALOG. */
1277 static svn_error_t *
1278 filter_log_entry_with_rangelist(void *baton,
1279                                 svn_log_entry_t *log_entry,
1280                                 apr_pool_t *pool)
1281 {
1282   struct filter_log_entry_baton_t *fleb = baton;
1283   svn_rangelist_t *intersection, *this_rangelist;
1284
1285   if (fleb->ctx->cancel_func)
1286     SVN_ERR(fleb->ctx->cancel_func(fleb->ctx->cancel_baton));
1287
1288   /* Ignore r0 because there can be no "change 0" in a merge range. */
1289   if (log_entry->revision == 0)
1290     return SVN_NO_ERROR;
1291
1292   this_rangelist = svn_rangelist__initialize(log_entry->revision - 1,
1293                                              log_entry->revision,
1294                                              TRUE, pool);
1295
1296   /* Don't consider inheritance yet, see if LOG_ENTRY->REVISION is
1297      fully or partially represented in BATON->RANGELIST. */
1298   SVN_ERR(svn_rangelist_intersect(&intersection, fleb->rangelist,
1299                                   this_rangelist, FALSE, pool));
1300   if (! (intersection && intersection->nelts))
1301     return SVN_NO_ERROR;
1302
1303   SVN_ERR_ASSERT(intersection->nelts == 1);
1304
1305   /* Ok, we know LOG_ENTRY->REVISION is represented in BATON->RANGELIST,
1306      but is it only partially represented, i.e. is the corresponding range in
1307      BATON->RANGELIST non-inheritable?  Ask for the same intersection as
1308      above but consider inheritance this time, if the intersection is empty
1309      we know the range in BATON->RANGELIST is non-inheritable. */
1310   SVN_ERR(svn_rangelist_intersect(&intersection, fleb->rangelist,
1311                                   this_rangelist, TRUE, pool));
1312   log_entry->non_inheritable = !intersection->nelts;
1313
1314   /* If the paths changed by LOG_ENTRY->REVISION are provided we can determine
1315      if LOG_ENTRY->REVISION, while only partially represented in
1316      BATON->RANGELIST, is in fact completely applied to all affected paths.
1317      ### And ... what if it is, or if it isn't? What do we do with the answer?
1318          And how do we cope if the changed paths are not provided? */
1319   if ((log_entry->non_inheritable || !fleb->filtering_merged)
1320       && log_entry->changed_paths2)
1321     {
1322       apr_hash_index_t *hi;
1323       svn_boolean_t all_subtrees_have_this_rev = TRUE;
1324       svn_rangelist_t *this_rev_rangelist =
1325         svn_rangelist__initialize(log_entry->revision - 1,
1326                                   log_entry->revision, TRUE, pool);
1327       apr_pool_t *iterpool = svn_pool_create(pool);
1328
1329       for (hi = apr_hash_first(pool, log_entry->changed_paths2);
1330            hi;
1331            hi = apr_hash_next(hi))
1332         {
1333           int i;
1334           const char *path = svn__apr_hash_index_key(hi);
1335           svn_log_changed_path2_t *change = svn__apr_hash_index_val(hi);
1336           const char *target_fspath_affected;
1337           svn_mergeinfo_t nearest_ancestor_mergeinfo;
1338           svn_boolean_t found_this_revision = FALSE;
1339           const char *merge_source_rel_target;
1340           const char *merge_source_fspath;
1341           svn_boolean_t ancestor_is_self;
1342
1343           svn_pool_clear(iterpool);
1344
1345           /* Check that PATH is a subtree of at least one of the
1346              merge sources.  If not then ignore this path.  */
1347           for (i = 0; i < fleb->merge_source_fspaths->nelts; i++)
1348             {
1349               merge_source_fspath = APR_ARRAY_IDX(fleb->merge_source_fspaths,
1350                                                   i, const char *);
1351
1352               merge_source_rel_target
1353                 = svn_fspath__skip_ancestor(merge_source_fspath, path);
1354               if (merge_source_rel_target)
1355                 {
1356                   /* If MERGE_SOURCE was itself deleted, replaced, or added
1357                      in LOG_ENTRY->REVISION then ignore this PATH since you
1358                      can't merge a addition or deletion of yourself. */
1359                   if (merge_source_rel_target[0] == '\0'
1360                       && (change->action != 'M'))
1361                     i = fleb->merge_source_fspaths->nelts;
1362                   break;
1363                 }
1364             }
1365           /* If we examined every merge source path and PATH is a child of
1366              none of them then we can ignore this PATH. */
1367           if (i == fleb->merge_source_fspaths->nelts)
1368             continue;
1369
1370           /* Calculate the target path which PATH would affect if merged. */
1371           target_fspath_affected = svn_fspath__join(fleb->target_fspath,
1372                                                     merge_source_rel_target,
1373                                                     iterpool);
1374
1375           nearest_ancestor_mergeinfo =
1376             find_nearest_ancestor(fleb->depth_first_catalog_index,
1377                                   &ancestor_is_self,
1378                                   target_fspath_affected);
1379
1380           /* Issue #3791: A path should never have explicit mergeinfo
1381              describing its own addition (that's self-referential).  Nor will
1382              it have explicit mergeinfo describing its own deletion (we
1383              obviously can't add new mergeinfo to a path we are deleting).
1384
1385              This lack of explicit mergeinfo should not cause such revisions
1386              to show up as eligible however.  If PATH was deleted, replaced,
1387              or added in LOG_ENTRY->REVISION, but the corresponding
1388              TARGET_PATH_AFFECTED already exists and has explicit mergeinfo
1389              describing merges from PATH *after* LOG_ENTRY->REVISION, then
1390              ignore this PATH.  If it was deleted in LOG_ENTRY->REVISION it's
1391              obviously back.  If it was added or replaced it's still around
1392              possibly it was replaced one or more times, but it's back now.
1393              Regardless, LOG_ENTRY->REVISION is *not* an eligible revision! */
1394           if (ancestor_is_self /* Explicit mergeinfo on TARGET_PATH_AFFECTED */
1395               && (change->action != 'M'))
1396             {
1397               svn_rangelist_t *rangelist =
1398                   svn_hash_gets(nearest_ancestor_mergeinfo, path);
1399               svn_merge_range_t *youngest_range = APR_ARRAY_IDX(
1400                 rangelist, rangelist->nelts - 1, svn_merge_range_t *);
1401
1402               if (youngest_range
1403                   && (youngest_range->end > log_entry->revision))
1404                 continue;
1405             }
1406
1407           if (nearest_ancestor_mergeinfo)
1408             {
1409               apr_hash_index_t *hi2;
1410
1411               for (hi2 = apr_hash_first(iterpool, nearest_ancestor_mergeinfo);
1412                    hi2;
1413                    hi2 = apr_hash_next(hi2))
1414                 {
1415                   const char *mergeinfo_path = svn__apr_hash_index_key(hi2);
1416                   svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi2);
1417
1418                   /* Does the mergeinfo for PATH reflect if
1419                      LOG_ENTRY->REVISION was previously merged
1420                      from MERGE_SOURCE_FSPATH? */
1421                   if (svn_fspath__skip_ancestor(merge_source_fspath,
1422                                                 mergeinfo_path))
1423                     {
1424                       /* Something was merged from MERGE_SOURCE_FSPATH, does
1425                          it include LOG_ENTRY->REVISION? */
1426                       SVN_ERR(svn_rangelist_intersect(&intersection,
1427                                                       rangelist,
1428                                                       this_rev_rangelist,
1429                                                       FALSE,
1430                                                       iterpool));
1431                       if (intersection->nelts)
1432                         {
1433                           if (ancestor_is_self)
1434                             {
1435                               /* TARGET_PATH_AFFECTED has explicit mergeinfo,
1436                                  so we don't need to worry if that mergeinfo
1437                                  is inheritable or not. */
1438                               found_this_revision = TRUE;
1439                               break;
1440                             }
1441                           else
1442                             {
1443                               /* TARGET_PATH_AFFECTED inherited its mergeinfo,
1444                                  so we have to ignore non-inheritable
1445                                  ranges. */
1446                               SVN_ERR(svn_rangelist_intersect(
1447                                 &intersection,
1448                                 rangelist,
1449                                 this_rev_rangelist,
1450                                 TRUE, iterpool));
1451                               if (intersection->nelts)
1452                                 {
1453                                   found_this_revision = TRUE;
1454                                   break;
1455                                 }
1456                             }
1457                         }
1458                     }
1459                 }
1460             }
1461
1462           if (!found_this_revision)
1463             {
1464               /* As soon as any PATH is found that is not fully merged for
1465                  LOG_ENTRY->REVISION then we can stop. */
1466               all_subtrees_have_this_rev = FALSE;
1467               break;
1468             }
1469         }
1470
1471       svn_pool_destroy(iterpool);
1472
1473       if (all_subtrees_have_this_rev)
1474         {
1475           if (fleb->filtering_merged)
1476             log_entry->non_inheritable = FALSE;
1477           else
1478             return SVN_NO_ERROR;
1479         }
1480     }
1481
1482   /* Call the wrapped log receiver which this function is filtering for. */
1483   return fleb->log_receiver(fleb->log_receiver_baton, log_entry, pool);
1484 }
1485
1486 static svn_error_t *
1487 logs_for_mergeinfo_rangelist(const char *source_url,
1488                              const apr_array_header_t *merge_source_fspaths,
1489                              svn_boolean_t filtering_merged,
1490                              const svn_rangelist_t *rangelist,
1491                              svn_boolean_t oldest_revs_first,
1492                              svn_mergeinfo_catalog_t target_mergeinfo_catalog,
1493                              const char *target_fspath,
1494                              svn_boolean_t discover_changed_paths,
1495                              const apr_array_header_t *revprops,
1496                              svn_log_entry_receiver_t log_receiver,
1497                              void *log_receiver_baton,
1498                              svn_client_ctx_t *ctx,
1499                              apr_pool_t *scratch_pool)
1500 {
1501   apr_array_header_t *target;
1502   svn_merge_range_t *oldest_range, *youngest_range;
1503   apr_array_header_t *revision_ranges;
1504   svn_opt_revision_t oldest_rev, youngest_rev;
1505   struct filter_log_entry_baton_t fleb;
1506
1507   if (! rangelist->nelts)
1508     return SVN_NO_ERROR;
1509
1510   /* Sort the rangelist. */
1511   qsort(rangelist->elts, rangelist->nelts,
1512         rangelist->elt_size, svn_sort_compare_ranges);
1513
1514   /* Build a single-member log target list using SOURCE_URL. */
1515   target = apr_array_make(scratch_pool, 1, sizeof(const char *));
1516   APR_ARRAY_PUSH(target, const char *) = source_url;
1517
1518   /* Calculate and construct the bounds of our log request. */
1519   youngest_range = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
1520                                  svn_merge_range_t *);
1521   youngest_rev.kind = svn_opt_revision_number;
1522   youngest_rev.value.number = youngest_range->end;
1523   oldest_range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
1524   oldest_rev.kind = svn_opt_revision_number;
1525   oldest_rev.value.number = oldest_range->start;
1526
1527   if (! target_mergeinfo_catalog)
1528     target_mergeinfo_catalog = apr_hash_make(scratch_pool);
1529
1530   /* FILTER_LOG_ENTRY_BATON_T->TARGET_MERGEINFO_CATALOG's keys are required
1531      to be repository-absolute. */
1532   SVN_ERR(svn_mergeinfo__add_prefix_to_catalog(&target_mergeinfo_catalog,
1533                                                target_mergeinfo_catalog, "/",
1534                                                scratch_pool, scratch_pool));
1535
1536   /* Build the log filtering callback baton. */
1537   fleb.filtering_merged = filtering_merged;
1538   fleb.merge_source_fspaths = merge_source_fspaths;
1539   fleb.target_mergeinfo_catalog = target_mergeinfo_catalog;
1540   fleb.depth_first_catalog_index =
1541     svn_sort__hash(target_mergeinfo_catalog,
1542                    svn_sort_compare_items_as_paths,
1543                    scratch_pool);
1544   fleb.target_fspath = target_fspath;
1545   fleb.rangelist = rangelist;
1546   fleb.log_receiver = log_receiver;
1547   fleb.log_receiver_baton = log_receiver_baton;
1548   fleb.ctx = ctx;
1549
1550   /* Drive the log. */
1551   revision_ranges = apr_array_make(scratch_pool, 1,
1552                                    sizeof(svn_opt_revision_range_t *));
1553   if (oldest_revs_first)
1554     APR_ARRAY_PUSH(revision_ranges, svn_opt_revision_range_t *)
1555       = svn_opt__revision_range_create(&oldest_rev, &youngest_rev, scratch_pool);
1556   else
1557     APR_ARRAY_PUSH(revision_ranges, svn_opt_revision_range_t *)
1558       = svn_opt__revision_range_create(&youngest_rev, &oldest_rev, scratch_pool);
1559   SVN_ERR(svn_client_log5(target, &youngest_rev, revision_ranges,
1560                           0, discover_changed_paths, FALSE, FALSE, revprops,
1561                           filter_log_entry_with_rangelist, &fleb, ctx,
1562                           scratch_pool));
1563
1564   /* Check for cancellation. */
1565   if (ctx->cancel_func)
1566     SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
1567
1568   return SVN_NO_ERROR;
1569 }
1570
1571 /* Set *OUT_MERGEINFO to a shallow copy of MERGEINFO with each source path
1572    converted to a (URI-encoded) URL based on REPOS_ROOT_URL. *OUT_MERGEINFO
1573    is declared as 'apr_hash_t *' because its key do not obey the rules of
1574    'svn_mergeinfo_t'.
1575
1576    Allocate *OUT_MERGEINFO and the new keys in RESULT_POOL.  Use
1577    SCRATCH_POOL for any temporary allocations. */
1578 static svn_error_t *
1579 mergeinfo_relpaths_to_urls(apr_hash_t **out_mergeinfo,
1580                            svn_mergeinfo_t mergeinfo,
1581                            const char *repos_root_url,
1582                            apr_pool_t *result_pool,
1583                            apr_pool_t *scratch_pool)
1584 {
1585   *out_mergeinfo = NULL;
1586   if (mergeinfo)
1587     {
1588       apr_hash_index_t *hi;
1589       apr_hash_t *full_path_mergeinfo = apr_hash_make(result_pool);
1590
1591       for (hi = apr_hash_first(scratch_pool, mergeinfo);
1592            hi; hi = apr_hash_next(hi))
1593         {
1594           const char *key = svn__apr_hash_index_key(hi);
1595           void *val = svn__apr_hash_index_val(hi);
1596
1597           svn_hash_sets(full_path_mergeinfo,
1598                         svn_path_url_add_component2(repos_root_url, key + 1,
1599                                                     result_pool),
1600                         val);
1601         }
1602       *out_mergeinfo = full_path_mergeinfo;
1603     }
1604
1605   return SVN_NO_ERROR;
1606 }
1607
1608 \f
1609 /*** Public APIs ***/
1610
1611 svn_error_t *
1612 svn_client_mergeinfo_get_merged(apr_hash_t **mergeinfo_p,
1613                                 const char *path_or_url,
1614                                 const svn_opt_revision_t *peg_revision,
1615                                 svn_client_ctx_t *ctx,
1616                                 apr_pool_t *pool)
1617 {
1618   const char *repos_root;
1619   svn_mergeinfo_catalog_t mergeinfo_cat;
1620   svn_mergeinfo_t mergeinfo;
1621
1622   SVN_ERR(get_mergeinfo(&mergeinfo_cat, &repos_root, path_or_url,
1623                         peg_revision, FALSE, FALSE, ctx, pool, pool));
1624   if (mergeinfo_cat)
1625     {
1626       const char *repos_relpath;
1627
1628       if (! svn_path_is_url(path_or_url))
1629         {
1630           SVN_ERR(svn_dirent_get_absolute(&path_or_url, path_or_url, pool));
1631           SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, NULL, NULL,
1632                                               ctx->wc_ctx, path_or_url,
1633                                               pool, pool));
1634         }
1635       else
1636         {
1637           repos_relpath = svn_uri_skip_ancestor(repos_root, path_or_url, pool);
1638
1639           SVN_ERR_ASSERT(repos_relpath != NULL); /* Or get_mergeinfo failed */
1640         }
1641
1642       mergeinfo = svn_hash_gets(mergeinfo_cat, repos_relpath);
1643     }
1644   else
1645     {
1646       mergeinfo = NULL;
1647     }
1648
1649   SVN_ERR(mergeinfo_relpaths_to_urls(mergeinfo_p, mergeinfo,
1650                                      repos_root, pool, pool));
1651   return SVN_NO_ERROR;
1652 }
1653
1654 svn_error_t *
1655 svn_client__mergeinfo_log(svn_boolean_t finding_merged,
1656                           const char *target_path_or_url,
1657                           const svn_opt_revision_t *target_peg_revision,
1658                           svn_mergeinfo_catalog_t *target_mergeinfo_catalog,
1659                           const char *source_path_or_url,
1660                           const svn_opt_revision_t *source_peg_revision,
1661                           const svn_opt_revision_t *source_start_revision,
1662                           const svn_opt_revision_t *source_end_revision,
1663                           svn_log_entry_receiver_t log_receiver,
1664                           void *log_receiver_baton,
1665                           svn_boolean_t discover_changed_paths,
1666                           svn_depth_t depth,
1667                           const apr_array_header_t *revprops,
1668                           svn_client_ctx_t *ctx,
1669                           apr_pool_t *result_pool,
1670                           apr_pool_t *scratch_pool)
1671 {
1672   const char *log_target = NULL;
1673   const char *repos_root;
1674   const char *target_repos_relpath;
1675   svn_mergeinfo_catalog_t target_mergeinfo_cat;
1676   svn_ra_session_t *target_session = NULL;
1677   svn_client__pathrev_t *pathrev;
1678
1679   /* A hash of paths, at or under TARGET_PATH_OR_URL, mapped to
1680      rangelists.  Not technically mergeinfo, so not using the
1681      svn_mergeinfo_t type. */
1682   apr_hash_t *inheritable_subtree_merges;
1683
1684   svn_mergeinfo_t source_history;
1685   svn_mergeinfo_t target_history;
1686   svn_rangelist_t *master_noninheritable_rangelist;
1687   svn_rangelist_t *master_inheritable_rangelist;
1688   apr_array_header_t *merge_source_fspaths =
1689     apr_array_make(scratch_pool, 1, sizeof(const char *));
1690   apr_hash_index_t *hi_catalog;
1691   apr_hash_index_t *hi;
1692   apr_pool_t *iterpool;
1693   svn_boolean_t oldest_revs_first = TRUE;
1694   apr_pool_t *subpool;
1695
1696   /* We currently only support depth = empty | infinity. */
1697   if (depth != svn_depth_infinity && depth != svn_depth_empty)
1698     return svn_error_create(
1699       SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1700       _("Only depths 'infinity' and 'empty' are currently supported"));
1701
1702   /* Validate and sanitize the incoming source operative revision range. */
1703   if (!((source_start_revision->kind == svn_opt_revision_unspecified) ||
1704         (source_start_revision->kind == svn_opt_revision_number) ||
1705         (source_start_revision->kind == svn_opt_revision_date) ||
1706         (source_start_revision->kind == svn_opt_revision_head)))
1707     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1708   if (!((source_end_revision->kind == svn_opt_revision_unspecified) ||
1709         (source_end_revision->kind == svn_opt_revision_number) ||
1710         (source_end_revision->kind == svn_opt_revision_date) ||
1711         (source_end_revision->kind == svn_opt_revision_head)))
1712     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1713   if ((source_end_revision->kind != svn_opt_revision_unspecified)
1714       && (source_start_revision->kind == svn_opt_revision_unspecified))
1715     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1716   if ((source_end_revision->kind == svn_opt_revision_unspecified)
1717       && (source_start_revision->kind != svn_opt_revision_unspecified))
1718     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1719
1720   subpool = svn_pool_create(scratch_pool);
1721
1722   /* We need the union of TARGET_PATH_OR_URL@TARGET_PEG_REVISION's mergeinfo
1723      and MERGE_SOURCE_URL's history.  It's not enough to do path
1724      matching, because renames in the history of MERGE_SOURCE_URL
1725      throw that all in a tizzy.  Of course, if there's no mergeinfo on
1726      the target, that vastly simplifies matters (we'll have nothing to
1727      do). */
1728   /* This get_mergeinfo() call doubles as a mergeinfo capabilities check. */
1729   if (target_mergeinfo_catalog)
1730     {
1731       if (*target_mergeinfo_catalog)
1732         {
1733           /* The caller provided the mergeinfo catalog for
1734              TARGET_PATH_OR_URL, so we don't need to accquire
1735              it ourselves.  We do need to get the repos_root
1736              though, because get_mergeinfo() won't do it for us. */
1737           target_mergeinfo_cat = *target_mergeinfo_catalog;
1738           SVN_ERR(svn_client__ra_session_from_path2(&target_session, &pathrev,
1739                                                     target_path_or_url, NULL,
1740                                                     target_peg_revision,
1741                                                     target_peg_revision,
1742                                                     ctx, subpool));
1743           SVN_ERR(svn_ra_get_repos_root2(target_session, &repos_root,
1744                                          scratch_pool));
1745         }
1746       else
1747         {
1748           /* The caller didn't provide the mergeinfo catalog for
1749              TARGET_PATH_OR_URL, but wants us to pass a copy back
1750              when we get it, so use RESULT_POOL. */
1751           SVN_ERR(get_mergeinfo(target_mergeinfo_catalog, &repos_root,
1752                                 target_path_or_url, target_peg_revision,
1753                                 depth == svn_depth_infinity, TRUE,
1754                                 ctx, result_pool, scratch_pool));
1755           target_mergeinfo_cat = *target_mergeinfo_catalog;
1756         }
1757     }
1758   else
1759     {
1760       /* The caller didn't provide the mergeinfo catalog for
1761          TARGET_PATH_OR_URL, nor does it want a copy, so we can use
1762          nothing but SCRATCH_POOL. */
1763       SVN_ERR(get_mergeinfo(&target_mergeinfo_cat, &repos_root,
1764                             target_path_or_url, target_peg_revision,
1765                             depth == svn_depth_infinity, TRUE,
1766                             ctx, scratch_pool, scratch_pool));
1767     }
1768
1769   if (!svn_path_is_url(target_path_or_url))
1770     {
1771       SVN_ERR(svn_dirent_get_absolute(&target_path_or_url,
1772                                       target_path_or_url, scratch_pool));
1773       SVN_ERR(svn_wc__node_get_repos_info(NULL, &target_repos_relpath,
1774                                           NULL, NULL,
1775                                           ctx->wc_ctx, target_path_or_url,
1776                                           scratch_pool, scratch_pool));
1777     }
1778   else
1779     {
1780       target_repos_relpath = svn_uri_skip_ancestor(repos_root,
1781                                                    target_path_or_url,
1782                                                    scratch_pool);
1783
1784       /* TARGET_REPOS_REL should be non-NULL, else get_mergeinfo
1785          should have failed.  */
1786       SVN_ERR_ASSERT(target_repos_relpath != NULL);
1787     }
1788
1789   if (!target_mergeinfo_cat)
1790     {
1791       /* If we are looking for what has been merged and there is no
1792          mergeinfo then we already know the answer.  If we are looking
1793          for eligible revisions then create a catalog with empty mergeinfo
1794          on the target.  This is semantically equivalent to no mergeinfo
1795          and gives us something to combine with MERGE_SOURCE_URL's
1796          history. */
1797       if (finding_merged)
1798         {
1799           svn_pool_destroy(subpool);
1800           return SVN_NO_ERROR;
1801         }
1802       else
1803         {
1804           target_mergeinfo_cat = apr_hash_make(scratch_pool);
1805           svn_hash_sets(target_mergeinfo_cat, target_repos_relpath,
1806                         apr_hash_make(scratch_pool));
1807         }
1808     }
1809
1810   /* Fetch the location history as mergeinfo, for the source branch
1811    * (between the given start and end revisions), and, if we're finding
1812    * merged revisions, then also for the entire target branch.
1813    *
1814    * ### TODO: As the source and target must be in the same repository, we
1815    * should share a single session, tracking the two URLs separately. */
1816   {
1817     svn_ra_session_t *source_session;
1818     svn_revnum_t start_rev, end_rev, youngest_rev = SVN_INVALID_REVNUM;
1819
1820     if (! finding_merged)
1821       {
1822         if (!target_session)
1823           SVN_ERR(svn_client__ra_session_from_path2(&target_session, &pathrev,
1824                                                     target_path_or_url, NULL,
1825                                                     target_peg_revision,
1826                                                     target_peg_revision,
1827                                                     ctx, subpool));
1828         SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history, NULL,
1829                                                      pathrev,
1830                                                      SVN_INVALID_REVNUM,
1831                                                      SVN_INVALID_REVNUM,
1832                                                      target_session, ctx,
1833                                                      scratch_pool));
1834       }
1835
1836     SVN_ERR(svn_client__ra_session_from_path2(&source_session, &pathrev,
1837                                               source_path_or_url, NULL,
1838                                               source_peg_revision,
1839                                               source_peg_revision,
1840                                               ctx, subpool));
1841     SVN_ERR(svn_client__get_revision_number(&start_rev, &youngest_rev,
1842                                             ctx->wc_ctx, source_path_or_url,
1843                                             source_session,
1844                                             source_start_revision,
1845                                             subpool));
1846     SVN_ERR(svn_client__get_revision_number(&end_rev, &youngest_rev,
1847                                             ctx->wc_ctx, source_path_or_url,
1848                                             source_session,
1849                                             source_end_revision,
1850                                             subpool));
1851     SVN_ERR(svn_client__get_history_as_mergeinfo(&source_history, NULL,
1852                                                  pathrev,
1853                                                  MAX(end_rev, start_rev),
1854                                                  MIN(end_rev, start_rev),
1855                                                  source_session, ctx,
1856                                                  scratch_pool));
1857     if (start_rev > end_rev)
1858       oldest_revs_first = FALSE;
1859
1860     /* Close the source and target sessions. */
1861     svn_pool_destroy(subpool);
1862   }
1863
1864   /* Separate the explicit or inherited mergeinfo on TARGET_PATH_OR_URL,
1865      and possibly its explicit subtree mergeinfo, into their
1866      inheritable and non-inheritable parts. */
1867   master_noninheritable_rangelist = apr_array_make(scratch_pool, 64,
1868                                                    sizeof(svn_merge_range_t *));
1869   master_inheritable_rangelist = apr_array_make(scratch_pool, 64,
1870                                                 sizeof(svn_merge_range_t *));
1871   inheritable_subtree_merges = apr_hash_make(scratch_pool);
1872
1873   iterpool = svn_pool_create(scratch_pool);
1874
1875   for (hi_catalog = apr_hash_first(scratch_pool, target_mergeinfo_cat);
1876        hi_catalog;
1877        hi_catalog = apr_hash_next(hi_catalog))
1878     {
1879       svn_mergeinfo_t subtree_mergeinfo = svn__apr_hash_index_val(hi_catalog);
1880       svn_mergeinfo_t subtree_history;
1881       svn_mergeinfo_t subtree_source_history;
1882       svn_mergeinfo_t subtree_inheritable_mergeinfo;
1883       svn_mergeinfo_t subtree_noninheritable_mergeinfo;
1884       svn_mergeinfo_t merged_noninheritable;
1885       svn_mergeinfo_t merged;
1886       const char *subtree_path = svn__apr_hash_index_key(hi_catalog);
1887       svn_boolean_t is_subtree = strcmp(subtree_path,
1888                                         target_repos_relpath) != 0;
1889       svn_pool_clear(iterpool);
1890
1891       if (is_subtree)
1892         {
1893           /* If SUBTREE_PATH is a proper subtree of TARGET_PATH_OR_URL
1894              then make a copy of SOURCE_HISTORY that is path adjusted
1895              for the subtree.  */
1896           const char *subtree_rel_path =
1897             subtree_path + strlen(target_repos_relpath) + 1;
1898
1899           SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
1900             &subtree_source_history, source_history,
1901             subtree_rel_path, scratch_pool, scratch_pool));
1902
1903           if (!finding_merged)
1904             SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
1905                     &subtree_history, target_history,
1906                     subtree_rel_path, scratch_pool, scratch_pool));
1907         }
1908       else
1909         {
1910           subtree_source_history = source_history;
1911           if (!finding_merged)
1912             subtree_history = target_history;
1913         }
1914
1915       if (!finding_merged)
1916         {
1917           svn_mergeinfo_t merged_via_history;
1918           SVN_ERR(svn_mergeinfo_intersect2(&merged_via_history,
1919                                            subtree_history,
1920                                            subtree_source_history, TRUE,
1921                                            scratch_pool, iterpool));
1922           SVN_ERR(svn_mergeinfo_merge2(subtree_mergeinfo,
1923                                        merged_via_history,
1924                                        scratch_pool, scratch_pool));
1925         }
1926
1927       SVN_ERR(svn_mergeinfo_inheritable2(&subtree_inheritable_mergeinfo,
1928                                          subtree_mergeinfo, NULL,
1929                                          SVN_INVALID_REVNUM,
1930                                          SVN_INVALID_REVNUM,
1931                                          TRUE, scratch_pool, iterpool));
1932       SVN_ERR(svn_mergeinfo_inheritable2(&subtree_noninheritable_mergeinfo,
1933                                          subtree_mergeinfo, NULL,
1934                                          SVN_INVALID_REVNUM,
1935                                          SVN_INVALID_REVNUM,
1936                                          FALSE, scratch_pool, iterpool));
1937
1938       /* Find the intersection of the non-inheritable part of
1939          SUBTREE_MERGEINFO and SOURCE_HISTORY.  svn_mergeinfo_intersect2()
1940          won't consider non-inheritable and inheritable ranges
1941          intersecting unless we ignore inheritance, but in doing so the
1942          resulting intersections have all inheritable ranges.  To get
1943          around this we set the inheritance on the result to all
1944          non-inheritable. */
1945       SVN_ERR(svn_mergeinfo_intersect2(&merged_noninheritable,
1946                                        subtree_noninheritable_mergeinfo,
1947                                        subtree_source_history, FALSE,
1948                                        scratch_pool, iterpool));
1949       svn_mergeinfo__set_inheritance(merged_noninheritable, FALSE,
1950                                      scratch_pool);
1951
1952       /* Keep track of all ranges partially merged to any and all
1953          subtrees. */
1954       SVN_ERR(svn_rangelist__merge_many(master_noninheritable_rangelist,
1955                                         merged_noninheritable,
1956                                         scratch_pool, iterpool));
1957
1958       /* Find the intersection of the inheritable part of TGT_MERGEINFO
1959          and SOURCE_HISTORY. */
1960       SVN_ERR(svn_mergeinfo_intersect2(&merged,
1961                                        subtree_inheritable_mergeinfo,
1962                                        subtree_source_history, FALSE,
1963                                        scratch_pool, iterpool));
1964
1965       /* Keep track of all ranges fully merged to any and all
1966          subtrees. */
1967       if (apr_hash_count(merged))
1968         {
1969           /* The inheritable rangelist merged from SUBTREE_SOURCE_HISTORY
1970              to SUBTREE_PATH. */
1971           svn_rangelist_t *subtree_merged_rangelist =
1972             apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
1973
1974           SVN_ERR(svn_rangelist__merge_many(master_inheritable_rangelist,
1975                                             merged, scratch_pool, iterpool));
1976           SVN_ERR(svn_rangelist__merge_many(subtree_merged_rangelist,
1977                                             merged, scratch_pool, iterpool));
1978
1979           svn_hash_sets(inheritable_subtree_merges, subtree_path,
1980                         subtree_merged_rangelist);
1981         }
1982       else
1983         {
1984           /* Map SUBTREE_PATH to an empty rangelist if there was nothing
1985              fully merged. e.g. Only empty or non-inheritable mergeinfo
1986              on the subtree or mergeinfo unrelated to the source. */
1987           svn_hash_sets(inheritable_subtree_merges, subtree_path,
1988                         apr_array_make(scratch_pool, 0,
1989                                        sizeof(svn_merge_range_t *)));
1990         }
1991     }
1992
1993   /* Make sure every range in MASTER_INHERITABLE_RANGELIST is fully merged to
1994      each subtree (including the target itself).  Any revisions which don't
1995      exist in *every* subtree are *potentially* only partially merged to the
1996      tree rooted at TARGET_PATH_OR_URL, so move those revisions to
1997      MASTER_NONINHERITABLE_RANGELIST.  It may turn out that that a revision
1998      was merged to the only subtree it affects, but we need to examine the
1999      logs to make this determination (which will be done by
2000      logs_for_mergeinfo_rangelist). */
2001   if (master_inheritable_rangelist->nelts)
2002     {
2003       for (hi = apr_hash_first(scratch_pool, inheritable_subtree_merges);
2004            hi;
2005            hi = apr_hash_next(hi))
2006         {
2007           svn_rangelist_t *deleted_rangelist;
2008           svn_rangelist_t *added_rangelist;
2009           svn_rangelist_t *subtree_merged_rangelist =
2010             svn__apr_hash_index_val(hi);
2011
2012           svn_pool_clear(iterpool);
2013
2014           SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
2015                                      master_inheritable_rangelist,
2016                                      subtree_merged_rangelist, TRUE,
2017                                      iterpool));
2018
2019           if (deleted_rangelist->nelts)
2020             {
2021               svn_rangelist__set_inheritance(deleted_rangelist, FALSE);
2022               SVN_ERR(svn_rangelist_merge2(master_noninheritable_rangelist,
2023                                            deleted_rangelist,
2024                                            scratch_pool, iterpool));
2025               SVN_ERR(svn_rangelist_remove(&master_inheritable_rangelist,
2026                                            deleted_rangelist,
2027                                            master_inheritable_rangelist,
2028                                            FALSE,
2029                                            scratch_pool));
2030             }
2031         }
2032     }
2033
2034   if (finding_merged)
2035     {
2036       /* Roll all the merged revisions into one rangelist. */
2037       SVN_ERR(svn_rangelist_merge2(master_inheritable_rangelist,
2038                                    master_noninheritable_rangelist,
2039                                    scratch_pool, scratch_pool));
2040
2041     }
2042   else
2043     {
2044       /* Create the starting rangelist for what might be eligible. */
2045       svn_rangelist_t *source_master_rangelist =
2046         apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
2047
2048       SVN_ERR(svn_rangelist__merge_many(source_master_rangelist,
2049                                         source_history,
2050                                         scratch_pool, scratch_pool));
2051
2052       /* From what might be eligible subtract what we know is
2053          partially merged and then merge that back. */
2054       SVN_ERR(svn_rangelist_remove(&source_master_rangelist,
2055                                    master_noninheritable_rangelist,
2056                                    source_master_rangelist,
2057                                    FALSE, scratch_pool));
2058       SVN_ERR(svn_rangelist_merge2(source_master_rangelist,
2059                                    master_noninheritable_rangelist,
2060                                    scratch_pool, scratch_pool));
2061       SVN_ERR(svn_rangelist_remove(&master_inheritable_rangelist,
2062                                    master_inheritable_rangelist,
2063                                    source_master_rangelist,
2064                                    TRUE, scratch_pool));
2065     }
2066
2067   /* Nothing merged?  Not even when considering shared history if
2068      looking for eligible revisions (i.e. !FINDING_MERGED)?  Then there
2069      is nothing more to do. */
2070   if (! master_inheritable_rangelist->nelts)
2071     {
2072       svn_pool_destroy(iterpool);
2073       return SVN_NO_ERROR;
2074     }
2075   else
2076     {
2077       /* Determine the correct (youngest) target for 'svn log'. */
2078       svn_merge_range_t *youngest_range
2079         = APR_ARRAY_IDX(master_inheritable_rangelist,
2080                         master_inheritable_rangelist->nelts - 1,
2081                         svn_merge_range_t *);
2082       svn_rangelist_t *youngest_rangelist =
2083         svn_rangelist__initialize(youngest_range->end - 1,
2084                                   youngest_range->end,
2085                                   youngest_range->inheritable,
2086                                   scratch_pool);;
2087
2088       for (hi = apr_hash_first(scratch_pool, source_history);
2089            hi;
2090            hi = apr_hash_next(hi))
2091         {
2092           const char *key = svn__apr_hash_index_key(hi);
2093           svn_rangelist_t *subtree_merged_rangelist =
2094             svn__apr_hash_index_val(hi);
2095           svn_rangelist_t *intersecting_rangelist;
2096
2097           svn_pool_clear(iterpool);
2098           SVN_ERR(svn_rangelist_intersect(&intersecting_rangelist,
2099                                           youngest_rangelist,
2100                                           subtree_merged_rangelist,
2101                                           FALSE, iterpool));
2102
2103           APR_ARRAY_PUSH(merge_source_fspaths, const char *) = key;
2104
2105           if (intersecting_rangelist->nelts)
2106             log_target = key;
2107         }
2108     }
2109
2110   svn_pool_destroy(iterpool);
2111
2112   /* Step 4: Finally, we run 'svn log' to drive our log receiver, but
2113      using a receiver filter to only allow revisions to pass through
2114      that are in our rangelist. */
2115   log_target = svn_path_url_add_component2(repos_root, log_target + 1,
2116                                            scratch_pool);
2117
2118   SVN_ERR(logs_for_mergeinfo_rangelist(log_target, merge_source_fspaths,
2119                                        finding_merged,
2120                                        master_inheritable_rangelist,
2121                                        oldest_revs_first,
2122                                        target_mergeinfo_cat,
2123                                        svn_fspath__join("/",
2124                                                         target_repos_relpath,
2125                                                         scratch_pool),
2126                                        discover_changed_paths,
2127                                        revprops,
2128                                        log_receiver, log_receiver_baton,
2129                                        ctx, scratch_pool));
2130   return SVN_NO_ERROR;
2131 }
2132
2133 svn_error_t *
2134 svn_client_mergeinfo_log2(svn_boolean_t finding_merged,
2135                           const char *target_path_or_url,
2136                           const svn_opt_revision_t *target_peg_revision,
2137                           const char *source_path_or_url,
2138                           const svn_opt_revision_t *source_peg_revision,
2139                           const svn_opt_revision_t *source_start_revision,
2140                           const svn_opt_revision_t *source_end_revision,
2141                           svn_log_entry_receiver_t log_receiver,
2142                           void *log_receiver_baton,
2143                           svn_boolean_t discover_changed_paths,
2144                           svn_depth_t depth,
2145                           const apr_array_header_t *revprops,
2146                           svn_client_ctx_t *ctx,
2147                           apr_pool_t *scratch_pool)
2148 {
2149   return svn_client__mergeinfo_log(finding_merged, target_path_or_url,
2150                                    target_peg_revision, NULL,
2151                                    source_path_or_url, source_peg_revision,
2152                                    source_start_revision, source_end_revision,
2153                                    log_receiver, log_receiver_baton,
2154                                    discover_changed_paths, depth, revprops,
2155                                    ctx, scratch_pool, scratch_pool);
2156 }
2157
2158 svn_error_t *
2159 svn_client_suggest_merge_sources(apr_array_header_t **suggestions,
2160                                  const char *path_or_url,
2161                                  const svn_opt_revision_t *peg_revision,
2162                                  svn_client_ctx_t *ctx,
2163                                  apr_pool_t *pool)
2164 {
2165   const char *repos_root;
2166   const char *copyfrom_path;
2167   apr_array_header_t *list;
2168   svn_revnum_t copyfrom_rev;
2169   svn_mergeinfo_catalog_t mergeinfo_cat;
2170   svn_mergeinfo_t mergeinfo;
2171   apr_hash_index_t *hi;
2172
2173   list = apr_array_make(pool, 1, sizeof(const char *));
2174
2175   /* In our ideal algorithm, the list of recommendations should be
2176      ordered by:
2177
2178         1. The most recent existing merge source.
2179         2. The copyfrom source (which will also be listed as a merge
2180            source if the copy was made with a 1.5+ client and server).
2181         3. All other merge sources, most recent to least recent.
2182
2183      However, determining the order of application of merge sources
2184      requires a new RA API.  Until such an API is available, our
2185      algorithm will be:
2186
2187         1. The copyfrom source.
2188         2. All remaining merge sources (unordered).
2189   */
2190
2191   /* ### TODO: Share ra_session batons to improve efficiency? */
2192   SVN_ERR(get_mergeinfo(&mergeinfo_cat, &repos_root, path_or_url,
2193                         peg_revision, FALSE, FALSE, ctx, pool, pool));
2194
2195   if (mergeinfo_cat && apr_hash_count(mergeinfo_cat))
2196     {
2197       /* We asked only for the PATH_OR_URL's mergeinfo, not any of its
2198          descendants.  So if there is anything in the catalog it is the
2199          mergeinfo for PATH_OR_URL. */
2200       mergeinfo = svn__apr_hash_index_val(apr_hash_first(pool, mergeinfo_cat));
2201     }
2202   else
2203     {
2204       mergeinfo = NULL;
2205     }
2206
2207   SVN_ERR(svn_client__get_copy_source(&copyfrom_path, &copyfrom_rev,
2208                                       path_or_url, peg_revision, ctx,
2209                                       pool, pool));
2210   if (copyfrom_path)
2211     {
2212       APR_ARRAY_PUSH(list, const char *) =
2213         svn_path_url_add_component2(repos_root, copyfrom_path, pool);
2214     }
2215
2216   if (mergeinfo)
2217     {
2218       for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2219         {
2220           const char *rel_path = svn__apr_hash_index_key(hi);
2221
2222           if (copyfrom_path == NULL || strcmp(rel_path, copyfrom_path) != 0)
2223             APR_ARRAY_PUSH(list, const char *) = \
2224               svn_path_url_add_component2(repos_root, rel_path + 1, pool);
2225         }
2226     }
2227
2228   *suggestions = list;
2229   return SVN_NO_ERROR;
2230 }
2231
2232 svn_error_t *
2233 svn_client__mergeinfo_status(svn_boolean_t *mergeinfo_changes,
2234                              svn_wc_context_t *wc_ctx,
2235                              const char *local_abspath,
2236                              apr_pool_t *scratch_pool)
2237 {
2238   apr_array_header_t *propchanges;
2239   int i;
2240
2241   *mergeinfo_changes = FALSE;
2242
2243   SVN_ERR(svn_wc_get_prop_diffs2(&propchanges, NULL, wc_ctx,
2244                                  local_abspath, scratch_pool, scratch_pool));
2245
2246   for (i = 0; i < propchanges->nelts; i++)
2247     {
2248       svn_prop_t prop = APR_ARRAY_IDX(propchanges, i, svn_prop_t);
2249       if (strcmp(prop.name, SVN_PROP_MERGEINFO) == 0)
2250         {
2251           *mergeinfo_changes = TRUE;
2252           break;
2253         }
2254     }
2255
2256   return SVN_NO_ERROR;
2257 }