]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_client/mergeinfo.c
MFC r275385 (by bapt):
[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_client_private.h"
42 #include "private/svn_opt_private.h"
43 #include "private/svn_mergeinfo_private.h"
44 #include "private/svn_ra_private.h"
45 #include "private/svn_sorts_private.h"
46 #include "private/svn_wc_private.h"
47 #include "private/svn_fspath.h"
48 #include "client.h"
49 #include "mergeinfo.h"
50 #include "svn_private_config.h"
51
52
53
54 svn_client__merge_path_t *
55 svn_client__merge_path_dup(const svn_client__merge_path_t *old,
56                            apr_pool_t *pool)
57 {
58   svn_client__merge_path_t *new = apr_pmemdup(pool, old, sizeof(*old));
59
60   new->abspath = apr_pstrdup(pool, old->abspath);
61   if (new->remaining_ranges)
62     new->remaining_ranges = svn_rangelist_dup(old->remaining_ranges, pool);
63   if (new->pre_merge_mergeinfo)
64     new->pre_merge_mergeinfo = svn_mergeinfo_dup(old->pre_merge_mergeinfo,
65                                                  pool);
66   if (new->implicit_mergeinfo)
67     new->implicit_mergeinfo = svn_mergeinfo_dup(old->implicit_mergeinfo,
68                                                 pool);
69
70   return new;
71 }
72
73 svn_client__merge_path_t *
74 svn_client__merge_path_create(const char *abspath,
75                               apr_pool_t *pool)
76 {
77   svn_client__merge_path_t *result = apr_pcalloc(pool, sizeof(*result));
78
79   result->abspath = apr_pstrdup(pool, abspath);
80   return result;
81 }
82
83 svn_error_t *
84 svn_client__parse_mergeinfo(svn_mergeinfo_t *mergeinfo,
85                             svn_wc_context_t *wc_ctx,
86                             const char *local_abspath,
87                             apr_pool_t *result_pool,
88                             apr_pool_t *scratch_pool)
89 {
90   const svn_string_t *propval;
91
92   *mergeinfo = NULL;
93
94   /* ### Use svn_wc_prop_get() would actually be sufficient for now.
95      ### DannyB thinks that later we'll need behavior more like
96      ### svn_client__get_prop_from_wc(). */
97   SVN_ERR(svn_wc_prop_get2(&propval, wc_ctx, local_abspath, SVN_PROP_MERGEINFO,
98                            scratch_pool, scratch_pool));
99   if (propval)
100     SVN_ERR(svn_mergeinfo_parse(mergeinfo, propval->data, result_pool));
101
102   return SVN_NO_ERROR;
103 }
104
105 svn_error_t *
106 svn_client__record_wc_mergeinfo(const char *local_abspath,
107                                 svn_mergeinfo_t mergeinfo,
108                                 svn_boolean_t do_notification,
109                                 svn_client_ctx_t *ctx,
110                                 apr_pool_t *scratch_pool)
111 {
112   svn_string_t *mergeinfo_str = NULL;
113   svn_boolean_t mergeinfo_changes = FALSE;
114
115   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
116
117   /* Convert MERGEINFO (if any) into text for storage as a property value. */
118   if (mergeinfo)
119     SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_str, mergeinfo, scratch_pool));
120
121   if (do_notification && ctx->notify_func2)
122     SVN_ERR(svn_client__mergeinfo_status(&mergeinfo_changes, ctx->wc_ctx,
123                                          local_abspath, scratch_pool));
124
125   /* Record the new mergeinfo in the WC. */
126   /* ### Later, we'll want behavior more analogous to
127      ### svn_client__get_prop_from_wc(). */
128   SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath, SVN_PROP_MERGEINFO,
129                            mergeinfo_str, svn_depth_empty,
130                            TRUE /* skip checks */, NULL,
131                            NULL, NULL /* cancellation */,
132                            NULL, NULL /* notification */,
133                            scratch_pool));
134
135   if (do_notification && ctx->notify_func2)
136     {
137       svn_wc_notify_t *notify =
138         svn_wc_create_notify(local_abspath,
139                              svn_wc_notify_merge_record_info,
140                              scratch_pool);
141       if (mergeinfo_changes)
142         notify->prop_state = svn_wc_notify_state_merged;
143       else
144         notify->prop_state = svn_wc_notify_state_changed;
145
146       ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
147     }
148
149   return SVN_NO_ERROR;
150 }
151
152 svn_error_t *
153 svn_client__record_wc_mergeinfo_catalog(apr_hash_t *result_catalog,
154                                         svn_client_ctx_t *ctx,
155                                         apr_pool_t *scratch_pool)
156 {
157   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
158
159   if (apr_hash_count(result_catalog))
160     {
161       int i;
162       apr_array_header_t *sorted_cat =
163         svn_sort__hash(result_catalog, svn_sort_compare_items_as_paths,
164                        scratch_pool);
165
166       /* Write the mergeinfo out in sorted order of the paths (presumably just
167        * so that the notifications are in a predictable, convenient order). */
168       for (i = 0; i < sorted_cat->nelts; i++)
169         {
170           svn_sort__item_t elt = APR_ARRAY_IDX(sorted_cat, i,
171                                                svn_sort__item_t);
172           svn_error_t *err;
173
174           svn_pool_clear(iterpool);
175           err = svn_client__record_wc_mergeinfo(elt.key, elt.value, TRUE,
176                                                 ctx, iterpool);
177
178           if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
179             {
180               /* PATH isn't just missing, it's not even versioned as far
181                  as this working copy knows.  But it was included in
182                  MERGES, which means that the server knows about it.
183                  Likely we don't have access to the source due to authz
184                  restrictions.  For now just clear the error and
185                  continue... */
186               svn_error_clear(err);
187             }
188           else
189             {
190               SVN_ERR(err);
191             }
192         }
193     }
194   svn_pool_destroy(iterpool);
195   return SVN_NO_ERROR;
196 }
197
198 /*-----------------------------------------------------------------------*/
199 \f
200 /*** Retrieving mergeinfo. ***/
201
202 svn_error_t *
203 svn_client__get_wc_mergeinfo(svn_mergeinfo_t *mergeinfo,
204                              svn_boolean_t *inherited_p,
205                              svn_mergeinfo_inheritance_t inherit,
206                              const char *local_abspath,
207                              const char *limit_abspath,
208                              const char **walked_path,
209                              svn_boolean_t ignore_invalid_mergeinfo,
210                              svn_client_ctx_t *ctx,
211                              apr_pool_t *result_pool,
212                              apr_pool_t *scratch_pool)
213 {
214   const char *walk_relpath = "";
215   svn_mergeinfo_t wc_mergeinfo;
216   svn_revnum_t base_revision;
217   apr_pool_t *iterpool;
218   svn_boolean_t inherited;
219
220   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
221   if (limit_abspath)
222     SVN_ERR_ASSERT(svn_dirent_is_absolute(limit_abspath));
223
224   SVN_ERR(svn_wc__node_get_base(NULL, &base_revision, NULL, NULL, NULL, NULL,
225                                 ctx->wc_ctx, local_abspath,
226                                 TRUE /* ignore_enoent */,
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 /* ignore_enoent */,
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, scratch_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 = apr_hash_this_key(hi);
433           svn_string_t *propval = apr_hash_this_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         apr_hash_this_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         apr_hash_this_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, 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_error_t *err;
925
926       /* Get the TARGET_WCPATH's explicit mergeinfo. */
927       err = svn_client__get_wc_mergeinfo(&target_mergeinfo, NULL,
928                                          svn_mergeinfo_explicit,
929                                          target_abspath,
930                                          NULL, NULL, FALSE,
931                                          ctx, pool, pool);
932       if (err)
933         {
934           if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
935             {
936               /* Issue #3896: If we attempt elision because invalid
937                  mergeinfo is present on TARGET_WCPATH, then don't let
938                  the merge fail, just skip the elision attempt. */
939               svn_error_clear(err);
940               return SVN_NO_ERROR;
941             }
942           else
943             {
944               return svn_error_trace(err);
945             }
946         }
947
948      /* If TARGET_WCPATH has no explicit mergeinfo, there's nothing to
949          elide, we're done. */
950       if (target_mergeinfo == NULL)
951         return SVN_NO_ERROR;
952
953       /* Get TARGET_WCPATH's inherited mergeinfo from the WC. */
954       err = svn_client__get_wc_mergeinfo(&mergeinfo, NULL,
955                                          svn_mergeinfo_nearest_ancestor,
956                                          target_abspath,
957                                          limit_abspath,
958                                          NULL, FALSE, ctx, pool, pool);
959       if (err)
960         {
961           if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
962             {
963               /* Issue #3896 again, but invalid mergeinfo is inherited. */
964               svn_error_clear(err);
965               return SVN_NO_ERROR;
966             }
967           else
968             {
969               return svn_error_trace(err);
970             }
971         }
972
973       /* If TARGET_WCPATH inherited no mergeinfo from the WC and we are
974          not limiting our search to the working copy then check if it
975          inherits any from the repos. */
976       if (!mergeinfo && !wc_elision_limit_abspath)
977         {
978           err = svn_client__get_wc_or_repos_mergeinfo(
979             &mergeinfo, NULL, NULL, TRUE,
980             svn_mergeinfo_nearest_ancestor,
981             NULL, target_abspath, ctx, pool);
982           if (err)
983             {
984               if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
985                 {
986                   /* Issue #3896 again, but invalid mergeinfo is inherited
987                      from the repository. */
988                   svn_error_clear(err);
989                   return SVN_NO_ERROR;
990                 }
991               else
992                 {
993                   return svn_error_trace(err);
994                 }
995             }
996         }
997
998       /* If there is nowhere to elide TARGET_WCPATH's mergeinfo to and
999          the elision is limited, then we are done.*/
1000       if (!mergeinfo && wc_elision_limit_abspath)
1001         return SVN_NO_ERROR;
1002
1003       SVN_ERR(elide_mergeinfo(mergeinfo, target_mergeinfo, target_abspath,
1004                               ctx, pool));
1005     }
1006   return SVN_NO_ERROR;
1007 }
1008
1009
1010 /* Set *MERGEINFO_CATALOG to the explicit or inherited mergeinfo for
1011    PATH_OR_URL@PEG_REVISION.  If INCLUDE_DESCENDANTS is true, also
1012    store in *MERGEINFO_CATALOG the explicit mergeinfo on any subtrees
1013    under PATH_OR_URL.  Key all mergeinfo in *MERGEINFO_CATALOG on
1014    repository relpaths.
1015
1016    If no mergeinfo is found then set *MERGEINFO_CATALOG to NULL.
1017
1018    Set *REPOS_ROOT to the root URL of the repository associated with
1019    PATH_OR_URL.
1020
1021    If RA_SESSION is NOT NULL and PATH_OR_URL refers to a URL, RA_SESSION
1022    (which must be of the repository containing PATH_OR_URL) will be used
1023    instead of a temporary RA session. Caller is responsible for reparenting
1024    the session if it wants to use it after the call.
1025
1026    Allocate *MERGEINFO_CATALOG and all its contents in RESULT_POOL.  Use
1027    SCRATCH_POOL for all temporary allocations.
1028
1029    Return SVN_ERR_UNSUPPORTED_FEATURE if the server does not support
1030    Merge Tracking.  */
1031 static svn_error_t *
1032 get_mergeinfo(svn_mergeinfo_catalog_t *mergeinfo_catalog,
1033               const char **repos_root,
1034               const char *path_or_url,
1035               const svn_opt_revision_t *peg_revision,
1036               svn_boolean_t include_descendants,
1037               svn_boolean_t ignore_invalid_mergeinfo,
1038               svn_client_ctx_t *ctx,
1039               svn_ra_session_t *ra_session,
1040               apr_pool_t *result_pool,
1041               apr_pool_t *scratch_pool)
1042 {
1043   const char *local_abspath;
1044   svn_boolean_t use_url = svn_path_is_url(path_or_url);
1045   svn_client__pathrev_t *peg_loc;
1046
1047   if (ra_session && svn_path_is_url(path_or_url))
1048     {
1049       SVN_ERR(svn_ra_reparent(ra_session, path_or_url, scratch_pool));
1050       SVN_ERR(svn_client__resolve_rev_and_url(&peg_loc, ra_session,
1051                                               path_or_url,
1052                                               peg_revision,
1053                                               peg_revision,
1054                                               ctx, scratch_pool));
1055     }
1056   else
1057     {
1058       SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &peg_loc,
1059                                                 path_or_url, NULL,
1060                                                 peg_revision,
1061                                                 peg_revision, ctx, scratch_pool));
1062     }
1063
1064   /* If PATH_OR_URL is as working copy path determine if we will need to
1065      contact the repository for the requested PEG_REVISION. */
1066   if (!use_url)
1067     {
1068       svn_client__pathrev_t *origin;
1069       SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
1070                                       scratch_pool));
1071
1072       SVN_ERR(svn_client__wc_node_get_origin(&origin, local_abspath, ctx,
1073                                              scratch_pool, scratch_pool));
1074       if (!origin
1075           || strcmp(origin->url, peg_loc->url) != 0
1076           || peg_loc->rev != origin->rev)
1077       {
1078         use_url = TRUE; /* Don't rely on local mergeinfo */
1079       }
1080     }
1081
1082   SVN_ERR(svn_ra_get_repos_root2(ra_session, repos_root, result_pool));
1083
1084   if (use_url)
1085     {
1086       SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
1087         mergeinfo_catalog, ra_session, peg_loc->url, peg_loc->rev,
1088         svn_mergeinfo_inherited, FALSE, include_descendants,
1089         result_pool, scratch_pool));
1090     }
1091   else /* ! svn_path_is_url() */
1092     {
1093       SVN_ERR(svn_client__get_wc_or_repos_mergeinfo_catalog(
1094         mergeinfo_catalog, NULL, NULL, include_descendants, FALSE,
1095         ignore_invalid_mergeinfo, svn_mergeinfo_inherited,
1096         ra_session, path_or_url, ctx,
1097         result_pool, scratch_pool));
1098     }
1099
1100   return SVN_NO_ERROR;
1101 }
1102
1103 /*** In-memory mergeinfo elision ***/
1104 svn_error_t *
1105 svn_client__elide_mergeinfo_catalog(svn_mergeinfo_catalog_t mergeinfo_catalog,
1106                                     apr_pool_t *scratch_pool)
1107 {
1108   apr_array_header_t *sorted_hash;
1109   apr_array_header_t *elidable_paths = apr_array_make(scratch_pool, 1,
1110                                                       sizeof(const char *));
1111   apr_array_header_t *dir_stack = apr_array_make(scratch_pool, 1,
1112                                                  sizeof(const char *));
1113   apr_pool_t *iterpool;
1114   int i;
1115
1116   /* Here's the general algorithm:
1117      Walk through the paths sorted in tree order.  For each path, pop
1118      the dir_stack until it is either empty or the top item contains a parent
1119      of the current path. Check to see if that mergeinfo is then elidable,
1120      and build the list of elidable mergeinfo based upon that determination.
1121      Finally, push the path of interest onto the stack, and continue. */
1122   sorted_hash = svn_sort__hash(mergeinfo_catalog,
1123                                svn_sort_compare_items_as_paths,
1124                                scratch_pool);
1125   iterpool = svn_pool_create(scratch_pool);
1126   for (i = 0; i < sorted_hash->nelts; i++)
1127     {
1128       svn_sort__item_t *item = &APR_ARRAY_IDX(sorted_hash, i,
1129                                               svn_sort__item_t);
1130       const char *path = item->key;
1131
1132       if (dir_stack->nelts > 0)
1133         {
1134           const char *top;
1135           const char *path_suffix;
1136           svn_boolean_t elides = FALSE;
1137
1138           svn_pool_clear(iterpool);
1139
1140           /* Pop off any paths which are not ancestors of PATH. */
1141           do
1142             {
1143               top = APR_ARRAY_IDX(dir_stack, dir_stack->nelts - 1,
1144                                           const char *);
1145               path_suffix = svn_dirent_is_child(top, path, NULL);
1146
1147               if (!path_suffix)
1148                 apr_array_pop(dir_stack);
1149             }
1150           while (dir_stack->nelts > 0 && !path_suffix);
1151
1152           /* If we have a path suffix, it means we haven't popped the stack
1153              clean. */
1154           if (path_suffix)
1155             {
1156               SVN_ERR(should_elide_mergeinfo(&elides,
1157                                          svn_hash_gets(mergeinfo_catalog, top),
1158                                          svn_hash_gets(mergeinfo_catalog, path),
1159                                          path_suffix,
1160                                          iterpool));
1161
1162               if (elides)
1163                 APR_ARRAY_PUSH(elidable_paths, const char *) = path;
1164             }
1165         }
1166
1167       APR_ARRAY_PUSH(dir_stack, const char *) = path;
1168     }
1169   svn_pool_destroy(iterpool);
1170
1171   /* Now remove the elidable paths from the catalog. */
1172   for (i = 0; i < elidable_paths->nelts; i++)
1173     {
1174       const char *path = APR_ARRAY_IDX(elidable_paths, i, const char *);
1175       svn_hash_sets(mergeinfo_catalog, path, NULL);
1176     }
1177
1178   return SVN_NO_ERROR;
1179 }
1180
1181
1182 /* Helper for filter_log_entry_with_rangelist().
1183
1184    DEPTH_FIRST_CATALOG_INDEX is an array of svn_sort__item_t's.  The keys are
1185    repository-absolute const char *paths, the values are svn_mergeinfo_t for
1186    each path.
1187
1188    Return a pointer to the mergeinfo value of the nearest path-wise ancestor
1189    of FSPATH in DEPTH_FIRST_CATALOG_INDEX.  A path is considered its
1190    own ancestor, so if a key exactly matches FSPATH, return that
1191    key's mergeinfo and set *ANCESTOR_IS_SELF to true (set it to false in all
1192    other cases).
1193
1194    If DEPTH_FIRST_CATALOG_INDEX is NULL, empty, or no ancestor is found, then
1195    return NULL. */
1196 static svn_mergeinfo_t
1197 find_nearest_ancestor(const apr_array_header_t *depth_first_catalog_index,
1198                       svn_boolean_t *ancestor_is_self,
1199                       const char *fspath)
1200 {
1201   int ancestor_index = -1;
1202
1203   *ancestor_is_self = FALSE;
1204
1205   if (depth_first_catalog_index)
1206     {
1207       int i;
1208
1209       for (i = 0; i < depth_first_catalog_index->nelts; i++)
1210         {
1211           svn_sort__item_t item = APR_ARRAY_IDX(depth_first_catalog_index, i,
1212                                                 svn_sort__item_t);
1213           if (svn_fspath__skip_ancestor(item.key, fspath))
1214             {
1215               ancestor_index = i;
1216
1217               /* There's no nearer ancestor than FSPATH itself. */
1218               if (strcmp(item.key, fspath) == 0)
1219                 {
1220                   *ancestor_is_self = TRUE;
1221                   break;
1222                 }
1223             }
1224
1225         }
1226     }
1227
1228   if (ancestor_index == -1)
1229     return NULL;
1230   else
1231     return (APR_ARRAY_IDX(depth_first_catalog_index,
1232                           ancestor_index,
1233                           svn_sort__item_t)).value;
1234 }
1235
1236 /* Baton for use with the filter_log_entry_with_rangelist()
1237    svn_log_entry_receiver_t callback. */
1238 struct filter_log_entry_baton_t
1239 {
1240   /* Is TRUE if RANGELIST describes potentially merged revisions, is FALSE
1241      if RANGELIST describes potentially eligible revisions. */
1242   svn_boolean_t filtering_merged;
1243
1244   /* Unsorted array of repository relative paths representing the merge
1245      sources.  There will be more than one source  */
1246   const apr_array_header_t *merge_source_fspaths;
1247
1248   /* The repository-absolute path we are calling svn_client_log5() on. */
1249   const char *target_fspath;
1250
1251   /* Mergeinfo catalog for the tree rooted at TARGET_FSPATH.
1252      The path keys must be repository-absolute. */
1253   svn_mergeinfo_catalog_t target_mergeinfo_catalog;
1254
1255   /* Depth first sorted array of svn_sort__item_t's for
1256      TARGET_MERGEINFO_CATALOG. */
1257   apr_array_header_t *depth_first_catalog_index;
1258
1259   /* A rangelist describing all the revisions potentially merged or
1260      potentially eligible for merging (see FILTERING_MERGED) based on
1261      the target's explicit or inherited mergeinfo. */
1262   const svn_rangelist_t *rangelist;
1263
1264   /* The wrapped svn_log_entry_receiver_t callback and baton which
1265      filter_log_entry_with_rangelist() is acting as a filter for. */
1266   svn_log_entry_receiver_t log_receiver;
1267   void *log_receiver_baton;
1268
1269   svn_client_ctx_t *ctx;
1270 };
1271
1272 /* Implements the svn_log_entry_receiver_t interface.  BATON is a
1273    `struct filter_log_entry_baton_t *'.
1274
1275    Call the wrapped log receiver BATON->log_receiver (with
1276    BATON->log_receiver_baton) if:
1277
1278    BATON->FILTERING_MERGED is FALSE and the changes represented by LOG_ENTRY
1279    have been fully merged from BATON->merge_source_fspaths to the WC target
1280    based on the mergeinfo for the WC contained in BATON->TARGET_MERGEINFO_CATALOG.
1281
1282    Or
1283
1284    BATON->FILTERING_MERGED is TRUE and the changes represented by LOG_ENTRY
1285    have not been merged, or only partially merged, from
1286    BATON->merge_source_fspaths to the WC target based on the mergeinfo for the
1287    WC contained in BATON->TARGET_MERGEINFO_CATALOG. */
1288 static svn_error_t *
1289 filter_log_entry_with_rangelist(void *baton,
1290                                 svn_log_entry_t *log_entry,
1291                                 apr_pool_t *pool)
1292 {
1293   struct filter_log_entry_baton_t *fleb = baton;
1294   svn_rangelist_t *intersection, *this_rangelist;
1295
1296   if (fleb->ctx->cancel_func)
1297     SVN_ERR(fleb->ctx->cancel_func(fleb->ctx->cancel_baton));
1298
1299   /* Ignore r0 because there can be no "change 0" in a merge range. */
1300   if (log_entry->revision == 0)
1301     return SVN_NO_ERROR;
1302
1303   this_rangelist = svn_rangelist__initialize(log_entry->revision - 1,
1304                                              log_entry->revision,
1305                                              TRUE, pool);
1306
1307   /* Don't consider inheritance yet, see if LOG_ENTRY->REVISION is
1308      fully or partially represented in BATON->RANGELIST. */
1309   SVN_ERR(svn_rangelist_intersect(&intersection, fleb->rangelist,
1310                                   this_rangelist, FALSE, pool));
1311   if (! (intersection && intersection->nelts))
1312     return SVN_NO_ERROR;
1313
1314   SVN_ERR_ASSERT(intersection->nelts == 1);
1315
1316   /* Ok, we know LOG_ENTRY->REVISION is represented in BATON->RANGELIST,
1317      but is it only partially represented, i.e. is the corresponding range in
1318      BATON->RANGELIST non-inheritable?  Ask for the same intersection as
1319      above but consider inheritance this time, if the intersection is empty
1320      we know the range in BATON->RANGELIST is non-inheritable. */
1321   SVN_ERR(svn_rangelist_intersect(&intersection, fleb->rangelist,
1322                                   this_rangelist, TRUE, pool));
1323   log_entry->non_inheritable = !intersection->nelts;
1324
1325   /* If the paths changed by LOG_ENTRY->REVISION are provided we can determine
1326      if LOG_ENTRY->REVISION, while only partially represented in
1327      BATON->RANGELIST, is in fact completely applied to all affected paths.
1328      ### And ... what if it is, or if it isn't? What do we do with the answer?
1329          And how do we cope if the changed paths are not provided? */
1330   if ((log_entry->non_inheritable || !fleb->filtering_merged)
1331       && log_entry->changed_paths2)
1332     {
1333       apr_hash_index_t *hi;
1334       svn_boolean_t all_subtrees_have_this_rev = TRUE;
1335       svn_rangelist_t *this_rev_rangelist =
1336         svn_rangelist__initialize(log_entry->revision - 1,
1337                                   log_entry->revision, TRUE, pool);
1338       apr_pool_t *iterpool = svn_pool_create(pool);
1339
1340       for (hi = apr_hash_first(pool, log_entry->changed_paths2);
1341            hi;
1342            hi = apr_hash_next(hi))
1343         {
1344           int i;
1345           const char *path = apr_hash_this_key(hi);
1346           svn_log_changed_path2_t *change = apr_hash_this_val(hi);
1347           const char *target_fspath_affected;
1348           svn_mergeinfo_t nearest_ancestor_mergeinfo;
1349           svn_boolean_t found_this_revision = FALSE;
1350           const char *merge_source_rel_target;
1351           const char *merge_source_fspath;
1352           svn_boolean_t ancestor_is_self;
1353
1354           svn_pool_clear(iterpool);
1355
1356           /* Check that PATH is a subtree of at least one of the
1357              merge sources.  If not then ignore this path.  */
1358           for (i = 0; i < fleb->merge_source_fspaths->nelts; i++)
1359             {
1360               merge_source_fspath = APR_ARRAY_IDX(fleb->merge_source_fspaths,
1361                                                   i, const char *);
1362
1363               merge_source_rel_target
1364                 = svn_fspath__skip_ancestor(merge_source_fspath, path);
1365               if (merge_source_rel_target)
1366                 {
1367                   /* If MERGE_SOURCE was itself deleted, replaced, or added
1368                      in LOG_ENTRY->REVISION then ignore this PATH since you
1369                      can't merge a addition or deletion of yourself. */
1370                   if (merge_source_rel_target[0] == '\0'
1371                       && (change->action != 'M'))
1372                     i = fleb->merge_source_fspaths->nelts;
1373                   break;
1374                 }
1375             }
1376           /* If we examined every merge source path and PATH is a child of
1377              none of them then we can ignore this PATH. */
1378           if (i == fleb->merge_source_fspaths->nelts)
1379             continue;
1380
1381           /* Calculate the target path which PATH would affect if merged. */
1382           target_fspath_affected = svn_fspath__join(fleb->target_fspath,
1383                                                     merge_source_rel_target,
1384                                                     iterpool);
1385
1386           nearest_ancestor_mergeinfo =
1387             find_nearest_ancestor(fleb->depth_first_catalog_index,
1388                                   &ancestor_is_self,
1389                                   target_fspath_affected);
1390
1391           /* Issue #3791: A path should never have explicit mergeinfo
1392              describing its own addition (that's self-referential).  Nor will
1393              it have explicit mergeinfo describing its own deletion (we
1394              obviously can't add new mergeinfo to a path we are deleting).
1395
1396              This lack of explicit mergeinfo should not cause such revisions
1397              to show up as eligible however.  If PATH was deleted, replaced,
1398              or added in LOG_ENTRY->REVISION, but the corresponding
1399              TARGET_PATH_AFFECTED already exists and has explicit mergeinfo
1400              describing merges from PATH *after* LOG_ENTRY->REVISION, then
1401              ignore this PATH.  If it was deleted in LOG_ENTRY->REVISION it's
1402              obviously back.  If it was added or replaced it's still around
1403              possibly it was replaced one or more times, but it's back now.
1404              Regardless, LOG_ENTRY->REVISION is *not* an eligible revision! */
1405           if (nearest_ancestor_mergeinfo &&
1406               ancestor_is_self /* Explicit mergeinfo on TARGET_PATH_AFFECTED */
1407               && (change->action != 'M'))
1408             {
1409               svn_rangelist_t *rangelist =
1410                   svn_hash_gets(nearest_ancestor_mergeinfo, path);
1411               if (rangelist)
1412                 {
1413                   svn_merge_range_t *youngest_range = APR_ARRAY_IDX(
1414                     rangelist, rangelist->nelts - 1, svn_merge_range_t *);
1415
1416                   if (youngest_range
1417                       && (youngest_range->end > log_entry->revision))
1418                     continue;
1419                 }
1420             }
1421
1422           if (nearest_ancestor_mergeinfo)
1423             {
1424               apr_hash_index_t *hi2;
1425
1426               for (hi2 = apr_hash_first(iterpool, nearest_ancestor_mergeinfo);
1427                    hi2;
1428                    hi2 = apr_hash_next(hi2))
1429                 {
1430                   const char *mergeinfo_path = apr_hash_this_key(hi2);
1431                   svn_rangelist_t *rangelist = apr_hash_this_val(hi2);
1432
1433                   /* Does the mergeinfo for PATH reflect if
1434                      LOG_ENTRY->REVISION was previously merged
1435                      from MERGE_SOURCE_FSPATH? */
1436                   if (svn_fspath__skip_ancestor(merge_source_fspath,
1437                                                 mergeinfo_path))
1438                     {
1439                       /* Something was merged from MERGE_SOURCE_FSPATH, does
1440                          it include LOG_ENTRY->REVISION? */
1441                       SVN_ERR(svn_rangelist_intersect(&intersection,
1442                                                       rangelist,
1443                                                       this_rev_rangelist,
1444                                                       FALSE,
1445                                                       iterpool));
1446                       if (intersection->nelts)
1447                         {
1448                           if (ancestor_is_self)
1449                             {
1450                               /* TARGET_PATH_AFFECTED has explicit mergeinfo,
1451                                  so we don't need to worry if that mergeinfo
1452                                  is inheritable or not. */
1453                               found_this_revision = TRUE;
1454                               break;
1455                             }
1456                           else
1457                             {
1458                               /* TARGET_PATH_AFFECTED inherited its mergeinfo,
1459                                  so we have to ignore non-inheritable
1460                                  ranges. */
1461                               SVN_ERR(svn_rangelist_intersect(
1462                                 &intersection,
1463                                 rangelist,
1464                                 this_rev_rangelist,
1465                                 TRUE, iterpool));
1466                               if (intersection->nelts)
1467                                 {
1468                                   found_this_revision = TRUE;
1469                                   break;
1470                                 }
1471                             }
1472                         }
1473                     }
1474                 }
1475             }
1476
1477           if (!found_this_revision)
1478             {
1479               /* As soon as any PATH is found that is not fully merged for
1480                  LOG_ENTRY->REVISION then we can stop. */
1481               all_subtrees_have_this_rev = FALSE;
1482               break;
1483             }
1484         }
1485
1486       svn_pool_destroy(iterpool);
1487
1488       if (all_subtrees_have_this_rev)
1489         {
1490           if (fleb->filtering_merged)
1491             log_entry->non_inheritable = FALSE;
1492           else
1493             return SVN_NO_ERROR;
1494         }
1495     }
1496
1497   /* Call the wrapped log receiver which this function is filtering for. */
1498   return fleb->log_receiver(fleb->log_receiver_baton, log_entry, pool);
1499 }
1500
1501 static svn_error_t *
1502 logs_for_mergeinfo_rangelist(const char *source_url,
1503                              const apr_array_header_t *merge_source_fspaths,
1504                              svn_boolean_t filtering_merged,
1505                              const svn_rangelist_t *rangelist,
1506                              svn_boolean_t oldest_revs_first,
1507                              svn_mergeinfo_catalog_t target_mergeinfo_catalog,
1508                              const char *target_fspath,
1509                              svn_boolean_t discover_changed_paths,
1510                              const apr_array_header_t *revprops,
1511                              svn_log_entry_receiver_t log_receiver,
1512                              void *log_receiver_baton,
1513                              svn_client_ctx_t *ctx,
1514                              svn_ra_session_t *ra_session,
1515                              apr_pool_t *scratch_pool)
1516 {
1517   svn_merge_range_t *oldest_range, *youngest_range;
1518   svn_revnum_t oldest_rev, youngest_rev;
1519   struct filter_log_entry_baton_t fleb;
1520
1521   if (! rangelist->nelts)
1522     return SVN_NO_ERROR;
1523
1524   /* Calculate and construct the bounds of our log request. */
1525   youngest_range = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
1526                                  svn_merge_range_t *);
1527   youngest_rev = youngest_range->end;
1528   oldest_range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
1529   oldest_rev = oldest_range->start;
1530
1531   if (! target_mergeinfo_catalog)
1532     target_mergeinfo_catalog = apr_hash_make(scratch_pool);
1533
1534   /* FILTER_LOG_ENTRY_BATON_T->TARGET_MERGEINFO_CATALOG's keys are required
1535      to be repository-absolute. */
1536   SVN_ERR(svn_mergeinfo__add_prefix_to_catalog(&target_mergeinfo_catalog,
1537                                                target_mergeinfo_catalog, "/",
1538                                                scratch_pool, scratch_pool));
1539
1540   /* Build the log filtering callback baton. */
1541   fleb.filtering_merged = filtering_merged;
1542   fleb.merge_source_fspaths = merge_source_fspaths;
1543   fleb.target_mergeinfo_catalog = target_mergeinfo_catalog;
1544   fleb.depth_first_catalog_index =
1545     svn_sort__hash(target_mergeinfo_catalog,
1546                    svn_sort_compare_items_as_paths,
1547                    scratch_pool);
1548   fleb.target_fspath = target_fspath;
1549   fleb.rangelist = rangelist;
1550   fleb.log_receiver = log_receiver;
1551   fleb.log_receiver_baton = log_receiver_baton;
1552   fleb.ctx = ctx;
1553
1554   if (!ra_session)
1555     SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, source_url,
1556                                                  NULL, NULL, FALSE, FALSE, ctx,
1557                                                  scratch_pool, scratch_pool));
1558   else
1559     SVN_ERR(svn_ra_reparent(ra_session, source_url, scratch_pool));
1560
1561   {
1562     apr_array_header_t *target;
1563     target = apr_array_make(scratch_pool, 1, sizeof(const char *));
1564     APR_ARRAY_PUSH(target, const char *) = "";
1565
1566     SVN_ERR(svn_ra_get_log2(ra_session, target,
1567                             oldest_revs_first ? oldest_rev : youngest_rev,
1568                             oldest_revs_first ? youngest_rev : oldest_rev,
1569                             0 /* limit */,
1570                             discover_changed_paths,
1571                             FALSE /* strict_node_history */,
1572                             FALSE /* include_merged_revisions */,
1573                             revprops,
1574                             filter_log_entry_with_rangelist, &fleb,
1575                             scratch_pool));
1576   }
1577
1578   /* Check for cancellation. */
1579   if (ctx->cancel_func)
1580     SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
1581
1582   return SVN_NO_ERROR;
1583 }
1584
1585 /* Set *OUT_MERGEINFO to a shallow copy of MERGEINFO with each source path
1586    converted to a (URI-encoded) URL based on REPOS_ROOT_URL. *OUT_MERGEINFO
1587    is declared as 'apr_hash_t *' because its key do not obey the rules of
1588    'svn_mergeinfo_t'.
1589
1590    Allocate *OUT_MERGEINFO and the new keys in RESULT_POOL.  Use
1591    SCRATCH_POOL for any temporary allocations. */
1592 static svn_error_t *
1593 mergeinfo_relpaths_to_urls(apr_hash_t **out_mergeinfo,
1594                            svn_mergeinfo_t mergeinfo,
1595                            const char *repos_root_url,
1596                            apr_pool_t *result_pool,
1597                            apr_pool_t *scratch_pool)
1598 {
1599   *out_mergeinfo = NULL;
1600   if (mergeinfo)
1601     {
1602       apr_hash_index_t *hi;
1603       apr_hash_t *full_path_mergeinfo = apr_hash_make(result_pool);
1604
1605       for (hi = apr_hash_first(scratch_pool, mergeinfo);
1606            hi; hi = apr_hash_next(hi))
1607         {
1608           const char *key = apr_hash_this_key(hi);
1609           void *val = apr_hash_this_val(hi);
1610
1611           svn_hash_sets(full_path_mergeinfo,
1612                         svn_path_url_add_component2(repos_root_url, key + 1,
1613                                                     result_pool),
1614                         val);
1615         }
1616       *out_mergeinfo = full_path_mergeinfo;
1617     }
1618
1619   return SVN_NO_ERROR;
1620 }
1621
1622 \f
1623 /*** Public APIs ***/
1624
1625 svn_error_t *
1626 svn_client_mergeinfo_get_merged(apr_hash_t **mergeinfo_p,
1627                                 const char *path_or_url,
1628                                 const svn_opt_revision_t *peg_revision,
1629                                 svn_client_ctx_t *ctx,
1630                                 apr_pool_t *pool)
1631 {
1632   const char *repos_root;
1633   svn_mergeinfo_catalog_t mergeinfo_cat;
1634   svn_mergeinfo_t mergeinfo;
1635
1636   SVN_ERR(get_mergeinfo(&mergeinfo_cat, &repos_root, path_or_url,
1637                         peg_revision, FALSE, FALSE, ctx, NULL, pool, pool));
1638   if (mergeinfo_cat)
1639     {
1640       const char *repos_relpath;
1641
1642       if (! svn_path_is_url(path_or_url))
1643         {
1644           SVN_ERR(svn_dirent_get_absolute(&path_or_url, path_or_url, pool));
1645           SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, NULL, NULL,
1646                                               ctx->wc_ctx, path_or_url,
1647                                               pool, pool));
1648         }
1649       else
1650         {
1651           repos_relpath = svn_uri_skip_ancestor(repos_root, path_or_url, pool);
1652
1653           SVN_ERR_ASSERT(repos_relpath != NULL); /* Or get_mergeinfo failed */
1654         }
1655
1656       mergeinfo = svn_hash_gets(mergeinfo_cat, repos_relpath);
1657     }
1658   else
1659     {
1660       mergeinfo = NULL;
1661     }
1662
1663   SVN_ERR(mergeinfo_relpaths_to_urls(mergeinfo_p, mergeinfo,
1664                                      repos_root, pool, pool));
1665   return SVN_NO_ERROR;
1666 }
1667
1668 svn_error_t *
1669 svn_client__mergeinfo_log(svn_boolean_t finding_merged,
1670                           const char *target_path_or_url,
1671                           const svn_opt_revision_t *target_peg_revision,
1672                           svn_mergeinfo_catalog_t *target_mergeinfo_catalog,
1673                           const char *source_path_or_url,
1674                           const svn_opt_revision_t *source_peg_revision,
1675                           const svn_opt_revision_t *source_start_revision,
1676                           const svn_opt_revision_t *source_end_revision,
1677                           svn_log_entry_receiver_t log_receiver,
1678                           void *log_receiver_baton,
1679                           svn_boolean_t discover_changed_paths,
1680                           svn_depth_t depth,
1681                           const apr_array_header_t *revprops,
1682                           svn_client_ctx_t *ctx,
1683                           svn_ra_session_t *ra_session,
1684                           apr_pool_t *result_pool,
1685                           apr_pool_t *scratch_pool)
1686 {
1687   const char *log_target = NULL;
1688   const char *repos_root;
1689   const char *target_repos_relpath;
1690   svn_mergeinfo_catalog_t target_mergeinfo_cat;
1691   svn_ra_session_t *target_session = NULL;
1692   svn_client__pathrev_t *pathrev;
1693
1694   /* A hash of paths, at or under TARGET_PATH_OR_URL, mapped to
1695      rangelists.  Not technically mergeinfo, so not using the
1696      svn_mergeinfo_t type. */
1697   apr_hash_t *inheritable_subtree_merges;
1698
1699   svn_mergeinfo_t source_history;
1700   svn_mergeinfo_t target_history;
1701   svn_rangelist_t *master_noninheritable_rangelist;
1702   svn_rangelist_t *master_inheritable_rangelist;
1703   apr_array_header_t *merge_source_fspaths =
1704     apr_array_make(scratch_pool, 1, sizeof(const char *));
1705   apr_hash_index_t *hi_catalog;
1706   apr_hash_index_t *hi;
1707   apr_pool_t *iterpool;
1708   svn_boolean_t oldest_revs_first = TRUE;
1709   apr_pool_t *subpool;
1710
1711   /* We currently only support depth = empty | infinity. */
1712   if (depth != svn_depth_infinity && depth != svn_depth_empty)
1713     return svn_error_create(
1714       SVN_ERR_UNSUPPORTED_FEATURE, NULL,
1715       _("Only depths 'infinity' and 'empty' are currently supported"));
1716
1717   /* Validate and sanitize the incoming source operative revision range. */
1718   if (!((source_start_revision->kind == svn_opt_revision_unspecified) ||
1719         (source_start_revision->kind == svn_opt_revision_number) ||
1720         (source_start_revision->kind == svn_opt_revision_date) ||
1721         (source_start_revision->kind == svn_opt_revision_head)))
1722     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1723   if (!((source_end_revision->kind == svn_opt_revision_unspecified) ||
1724         (source_end_revision->kind == svn_opt_revision_number) ||
1725         (source_end_revision->kind == svn_opt_revision_date) ||
1726         (source_end_revision->kind == svn_opt_revision_head)))
1727     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1728   if ((source_end_revision->kind != svn_opt_revision_unspecified)
1729       && (source_start_revision->kind == svn_opt_revision_unspecified))
1730     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1731   if ((source_end_revision->kind == svn_opt_revision_unspecified)
1732       && (source_start_revision->kind != svn_opt_revision_unspecified))
1733     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
1734
1735   subpool = svn_pool_create(scratch_pool);
1736
1737   if (ra_session)
1738     target_session = ra_session;
1739
1740   /* We need the union of TARGET_PATH_OR_URL@TARGET_PEG_REVISION's mergeinfo
1741      and MERGE_SOURCE_URL's history.  It's not enough to do path
1742      matching, because renames in the history of MERGE_SOURCE_URL
1743      throw that all in a tizzy.  Of course, if there's no mergeinfo on
1744      the target, that vastly simplifies matters (we'll have nothing to
1745      do). */
1746   /* This get_mergeinfo() call doubles as a mergeinfo capabilities check. */
1747   if (target_mergeinfo_catalog)
1748     {
1749       if (*target_mergeinfo_catalog)
1750         {
1751           /* The caller provided the mergeinfo catalog for
1752              TARGET_PATH_OR_URL, so we don't need to accquire
1753              it ourselves.  We do need to get the repos_root
1754              though, because get_mergeinfo() won't do it for us. */
1755           target_mergeinfo_cat = *target_mergeinfo_catalog;
1756
1757           if (ra_session && svn_path_is_url(target_path_or_url))
1758             {
1759               SVN_ERR(svn_ra_reparent(ra_session, target_path_or_url, subpool));
1760               SVN_ERR(svn_client__resolve_rev_and_url(&pathrev, ra_session,
1761                                                       target_path_or_url,
1762                                                       target_peg_revision,
1763                                                       target_peg_revision,
1764                                                       ctx, subpool));
1765               target_session = ra_session;
1766             }
1767           else
1768             {
1769               SVN_ERR(svn_client__ra_session_from_path2(&target_session,
1770                                                         &pathrev,
1771                                                         target_path_or_url,
1772                                                         NULL,
1773                                                         target_peg_revision,
1774                                                         target_peg_revision,
1775                                                         ctx, subpool));
1776             }
1777           SVN_ERR(svn_ra_get_repos_root2(target_session, &repos_root,
1778                                          scratch_pool));
1779         }
1780       else
1781         {
1782           /* The caller didn't provide the mergeinfo catalog for
1783              TARGET_PATH_OR_URL, but wants us to pass a copy back
1784              when we get it, so use RESULT_POOL. */
1785           SVN_ERR(get_mergeinfo(target_mergeinfo_catalog, &repos_root,
1786                                 target_path_or_url, target_peg_revision,
1787                                 depth == svn_depth_infinity, TRUE,
1788                                 ctx, ra_session, result_pool, scratch_pool));
1789           target_mergeinfo_cat = *target_mergeinfo_catalog;
1790         }
1791     }
1792   else
1793     {
1794       /* The caller didn't provide the mergeinfo catalog for
1795          TARGET_PATH_OR_URL, nor does it want a copy, so we can use
1796          nothing but SCRATCH_POOL. */
1797       SVN_ERR(get_mergeinfo(&target_mergeinfo_cat, &repos_root,
1798                             target_path_or_url, target_peg_revision,
1799                             depth == svn_depth_infinity, TRUE,
1800                             ctx, ra_session, scratch_pool, scratch_pool));
1801     }
1802
1803   if (!svn_path_is_url(target_path_or_url))
1804     {
1805       SVN_ERR(svn_dirent_get_absolute(&target_path_or_url,
1806                                       target_path_or_url, scratch_pool));
1807       SVN_ERR(svn_wc__node_get_repos_info(NULL, &target_repos_relpath,
1808                                           NULL, NULL,
1809                                           ctx->wc_ctx, target_path_or_url,
1810                                           scratch_pool, scratch_pool));
1811     }
1812   else
1813     {
1814       target_repos_relpath = svn_uri_skip_ancestor(repos_root,
1815                                                    target_path_or_url,
1816                                                    scratch_pool);
1817
1818       /* TARGET_REPOS_REL should be non-NULL, else get_mergeinfo
1819          should have failed.  */
1820       SVN_ERR_ASSERT(target_repos_relpath != NULL);
1821     }
1822
1823   if (!target_mergeinfo_cat)
1824     {
1825       /* If we are looking for what has been merged and there is no
1826          mergeinfo then we already know the answer.  If we are looking
1827          for eligible revisions then create a catalog with empty mergeinfo
1828          on the target.  This is semantically equivalent to no mergeinfo
1829          and gives us something to combine with MERGE_SOURCE_URL's
1830          history. */
1831       if (finding_merged)
1832         {
1833           svn_pool_destroy(subpool);
1834           return SVN_NO_ERROR;
1835         }
1836       else
1837         {
1838           target_mergeinfo_cat = apr_hash_make(scratch_pool);
1839           svn_hash_sets(target_mergeinfo_cat, target_repos_relpath,
1840                         apr_hash_make(scratch_pool));
1841         }
1842     }
1843
1844   /* Fetch the location history as mergeinfo, for the source branch
1845    * (between the given start and end revisions), and, if we're finding
1846    * merged revisions, then also for the entire target branch.
1847    *
1848    * ### TODO: As the source and target must be in the same repository, we
1849    * should share a single session, tracking the two URLs separately. */
1850   {
1851     svn_ra_session_t *source_session;
1852     svn_revnum_t start_rev, end_rev, youngest_rev = SVN_INVALID_REVNUM;
1853
1854     if (! finding_merged)
1855       {
1856         if (!target_session)
1857           SVN_ERR(svn_client__ra_session_from_path2(&target_session, &pathrev,
1858                                                     target_path_or_url, NULL,
1859                                                     target_peg_revision,
1860                                                     target_peg_revision,
1861                                                     ctx, subpool));
1862         SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history, NULL,
1863                                                      pathrev,
1864                                                      SVN_INVALID_REVNUM,
1865                                                      SVN_INVALID_REVNUM,
1866                                                      target_session, ctx,
1867                                                      scratch_pool));
1868       }
1869
1870     if (target_session
1871         && svn_path_is_url(source_path_or_url)
1872         && repos_root
1873         && svn_uri_skip_ancestor(repos_root, source_path_or_url, subpool))
1874       {
1875         /* We can re-use the existing session */
1876         source_session = target_session;
1877         SVN_ERR(svn_ra_reparent(source_session, source_path_or_url, subpool));
1878         SVN_ERR(svn_client__resolve_rev_and_url(&pathrev, source_session,
1879                                                 source_path_or_url,
1880                                                 source_peg_revision,
1881                                                 source_peg_revision,
1882                                                 ctx, subpool));
1883       }
1884     else
1885       {
1886         SVN_ERR(svn_client__ra_session_from_path2(&source_session, &pathrev,
1887                                                   source_path_or_url, NULL,
1888                                                   source_peg_revision,
1889                                                   source_peg_revision,
1890                                                   ctx, subpool));
1891       }
1892     SVN_ERR(svn_client__get_revision_number(&start_rev, &youngest_rev,
1893                                             ctx->wc_ctx, source_path_or_url,
1894                                             source_session,
1895                                             source_start_revision,
1896                                             subpool));
1897     SVN_ERR(svn_client__get_revision_number(&end_rev, &youngest_rev,
1898                                             ctx->wc_ctx, source_path_or_url,
1899                                             source_session,
1900                                             source_end_revision,
1901                                             subpool));
1902     SVN_ERR(svn_client__get_history_as_mergeinfo(&source_history, NULL,
1903                                                  pathrev,
1904                                                  MAX(end_rev, start_rev),
1905                                                  MIN(end_rev, start_rev),
1906                                                  source_session, ctx,
1907                                                  scratch_pool));
1908     if (start_rev > end_rev)
1909       oldest_revs_first = FALSE;
1910   }
1911
1912   /* Separate the explicit or inherited mergeinfo on TARGET_PATH_OR_URL,
1913      and possibly its explicit subtree mergeinfo, into their
1914      inheritable and non-inheritable parts. */
1915   master_noninheritable_rangelist = apr_array_make(scratch_pool, 64,
1916                                                    sizeof(svn_merge_range_t *));
1917   master_inheritable_rangelist = apr_array_make(scratch_pool, 64,
1918                                                 sizeof(svn_merge_range_t *));
1919   inheritable_subtree_merges = apr_hash_make(scratch_pool);
1920
1921   iterpool = svn_pool_create(scratch_pool);
1922
1923   for (hi_catalog = apr_hash_first(scratch_pool, target_mergeinfo_cat);
1924        hi_catalog;
1925        hi_catalog = apr_hash_next(hi_catalog))
1926     {
1927       svn_mergeinfo_t subtree_mergeinfo = apr_hash_this_val(hi_catalog);
1928       svn_mergeinfo_t subtree_history;
1929       svn_mergeinfo_t subtree_source_history;
1930       svn_mergeinfo_t subtree_inheritable_mergeinfo;
1931       svn_mergeinfo_t subtree_noninheritable_mergeinfo;
1932       svn_mergeinfo_t merged_noninheritable;
1933       svn_mergeinfo_t merged;
1934       const char *subtree_path = apr_hash_this_key(hi_catalog);
1935       svn_boolean_t is_subtree = strcmp(subtree_path,
1936                                         target_repos_relpath) != 0;
1937       svn_pool_clear(iterpool);
1938
1939       if (is_subtree)
1940         {
1941           /* If SUBTREE_PATH is a proper subtree of TARGET_PATH_OR_URL
1942              then make a copy of SOURCE_HISTORY that is path adjusted
1943              for the subtree.  */
1944           const char *subtree_rel_path =
1945             subtree_path + strlen(target_repos_relpath) + 1;
1946
1947           SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
1948             &subtree_source_history, source_history,
1949             subtree_rel_path, scratch_pool, scratch_pool));
1950
1951           if (!finding_merged)
1952             SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
1953                     &subtree_history, target_history,
1954                     subtree_rel_path, scratch_pool, scratch_pool));
1955         }
1956       else
1957         {
1958           subtree_source_history = source_history;
1959           if (!finding_merged)
1960             subtree_history = target_history;
1961         }
1962
1963       if (!finding_merged)
1964         {
1965           svn_mergeinfo_t merged_via_history;
1966           SVN_ERR(svn_mergeinfo_intersect2(&merged_via_history,
1967                                            subtree_history,
1968                                            subtree_source_history, TRUE,
1969                                            scratch_pool, iterpool));
1970           SVN_ERR(svn_mergeinfo_merge2(subtree_mergeinfo,
1971                                        merged_via_history,
1972                                        scratch_pool, scratch_pool));
1973         }
1974
1975       SVN_ERR(svn_mergeinfo_inheritable2(&subtree_inheritable_mergeinfo,
1976                                          subtree_mergeinfo, NULL,
1977                                          SVN_INVALID_REVNUM,
1978                                          SVN_INVALID_REVNUM,
1979                                          TRUE, scratch_pool, iterpool));
1980       SVN_ERR(svn_mergeinfo_inheritable2(&subtree_noninheritable_mergeinfo,
1981                                          subtree_mergeinfo, NULL,
1982                                          SVN_INVALID_REVNUM,
1983                                          SVN_INVALID_REVNUM,
1984                                          FALSE, scratch_pool, iterpool));
1985
1986       /* Find the intersection of the non-inheritable part of
1987          SUBTREE_MERGEINFO and SOURCE_HISTORY.  svn_mergeinfo_intersect2()
1988          won't consider non-inheritable and inheritable ranges
1989          intersecting unless we ignore inheritance, but in doing so the
1990          resulting intersections have all inheritable ranges.  To get
1991          around this we set the inheritance on the result to all
1992          non-inheritable. */
1993       SVN_ERR(svn_mergeinfo_intersect2(&merged_noninheritable,
1994                                        subtree_noninheritable_mergeinfo,
1995                                        subtree_source_history, FALSE,
1996                                        scratch_pool, iterpool));
1997       svn_mergeinfo__set_inheritance(merged_noninheritable, FALSE,
1998                                      scratch_pool);
1999
2000       /* Keep track of all ranges partially merged to any and all
2001          subtrees. */
2002       SVN_ERR(svn_rangelist__merge_many(master_noninheritable_rangelist,
2003                                         merged_noninheritable,
2004                                         scratch_pool, iterpool));
2005
2006       /* Find the intersection of the inheritable part of TGT_MERGEINFO
2007          and SOURCE_HISTORY. */
2008       SVN_ERR(svn_mergeinfo_intersect2(&merged,
2009                                        subtree_inheritable_mergeinfo,
2010                                        subtree_source_history, FALSE,
2011                                        scratch_pool, iterpool));
2012
2013       /* Keep track of all ranges fully merged to any and all
2014          subtrees. */
2015       if (apr_hash_count(merged))
2016         {
2017           /* The inheritable rangelist merged from SUBTREE_SOURCE_HISTORY
2018              to SUBTREE_PATH. */
2019           svn_rangelist_t *subtree_merged_rangelist =
2020             apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
2021
2022           SVN_ERR(svn_rangelist__merge_many(master_inheritable_rangelist,
2023                                             merged, scratch_pool, iterpool));
2024           SVN_ERR(svn_rangelist__merge_many(subtree_merged_rangelist,
2025                                             merged, scratch_pool, iterpool));
2026
2027           svn_hash_sets(inheritable_subtree_merges, subtree_path,
2028                         subtree_merged_rangelist);
2029         }
2030       else
2031         {
2032           /* Map SUBTREE_PATH to an empty rangelist if there was nothing
2033              fully merged. e.g. Only empty or non-inheritable mergeinfo
2034              on the subtree or mergeinfo unrelated to the source. */
2035           svn_hash_sets(inheritable_subtree_merges, subtree_path,
2036                         apr_array_make(scratch_pool, 0,
2037                                        sizeof(svn_merge_range_t *)));
2038         }
2039     }
2040
2041   /* Make sure every range in MASTER_INHERITABLE_RANGELIST is fully merged to
2042      each subtree (including the target itself).  Any revisions which don't
2043      exist in *every* subtree are *potentially* only partially merged to the
2044      tree rooted at TARGET_PATH_OR_URL, so move those revisions to
2045      MASTER_NONINHERITABLE_RANGELIST.  It may turn out that that a revision
2046      was merged to the only subtree it affects, but we need to examine the
2047      logs to make this determination (which will be done by
2048      logs_for_mergeinfo_rangelist). */
2049   if (master_inheritable_rangelist->nelts)
2050     {
2051       for (hi = apr_hash_first(scratch_pool, inheritable_subtree_merges);
2052            hi;
2053            hi = apr_hash_next(hi))
2054         {
2055           svn_rangelist_t *deleted_rangelist;
2056           svn_rangelist_t *added_rangelist;
2057           svn_rangelist_t *subtree_merged_rangelist = apr_hash_this_val(hi);
2058
2059           svn_pool_clear(iterpool);
2060
2061           SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
2062                                      master_inheritable_rangelist,
2063                                      subtree_merged_rangelist, TRUE,
2064                                      iterpool));
2065
2066           if (deleted_rangelist->nelts)
2067             {
2068               svn_rangelist__set_inheritance(deleted_rangelist, FALSE);
2069               SVN_ERR(svn_rangelist_merge2(master_noninheritable_rangelist,
2070                                            deleted_rangelist,
2071                                            scratch_pool, iterpool));
2072               SVN_ERR(svn_rangelist_remove(&master_inheritable_rangelist,
2073                                            deleted_rangelist,
2074                                            master_inheritable_rangelist,
2075                                            FALSE,
2076                                            scratch_pool));
2077             }
2078         }
2079     }
2080
2081   if (finding_merged)
2082     {
2083       /* Roll all the merged revisions into one rangelist. */
2084       SVN_ERR(svn_rangelist_merge2(master_inheritable_rangelist,
2085                                    master_noninheritable_rangelist,
2086                                    scratch_pool, scratch_pool));
2087
2088     }
2089   else
2090     {
2091       /* Create the starting rangelist for what might be eligible. */
2092       svn_rangelist_t *source_master_rangelist =
2093         apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
2094
2095       SVN_ERR(svn_rangelist__merge_many(source_master_rangelist,
2096                                         source_history,
2097                                         scratch_pool, scratch_pool));
2098
2099       /* From what might be eligible subtract what we know is
2100          partially merged and then merge that back. */
2101       SVN_ERR(svn_rangelist_remove(&source_master_rangelist,
2102                                    master_noninheritable_rangelist,
2103                                    source_master_rangelist,
2104                                    FALSE, scratch_pool));
2105       SVN_ERR(svn_rangelist_merge2(source_master_rangelist,
2106                                    master_noninheritable_rangelist,
2107                                    scratch_pool, scratch_pool));
2108       SVN_ERR(svn_rangelist_remove(&master_inheritable_rangelist,
2109                                    master_inheritable_rangelist,
2110                                    source_master_rangelist,
2111                                    TRUE, scratch_pool));
2112     }
2113
2114   /* Nothing merged?  Not even when considering shared history if
2115      looking for eligible revisions (i.e. !FINDING_MERGED)?  Then there
2116      is nothing more to do. */
2117   if (! master_inheritable_rangelist->nelts)
2118     {
2119       svn_pool_destroy(iterpool);
2120       return SVN_NO_ERROR;
2121     }
2122   else
2123     {
2124       /* Determine the correct (youngest) target for 'svn log'. */
2125       svn_merge_range_t *youngest_range
2126         = APR_ARRAY_IDX(master_inheritable_rangelist,
2127                         master_inheritable_rangelist->nelts - 1,
2128                         svn_merge_range_t *);
2129       svn_rangelist_t *youngest_rangelist =
2130         svn_rangelist__initialize(youngest_range->end - 1,
2131                                   youngest_range->end,
2132                                   youngest_range->inheritable,
2133                                   scratch_pool);
2134
2135       for (hi = apr_hash_first(scratch_pool, source_history);
2136            hi;
2137            hi = apr_hash_next(hi))
2138         {
2139           const char *key = apr_hash_this_key(hi);
2140           svn_rangelist_t *subtree_merged_rangelist = apr_hash_this_val(hi);
2141           svn_rangelist_t *intersecting_rangelist;
2142
2143           svn_pool_clear(iterpool);
2144           SVN_ERR(svn_rangelist_intersect(&intersecting_rangelist,
2145                                           youngest_rangelist,
2146                                           subtree_merged_rangelist,
2147                                           FALSE, iterpool));
2148
2149           APR_ARRAY_PUSH(merge_source_fspaths, const char *) = key;
2150
2151           if (intersecting_rangelist->nelts)
2152             log_target = key;
2153         }
2154     }
2155
2156   svn_pool_destroy(iterpool);
2157
2158   /* Step 4: Finally, we run 'svn log' to drive our log receiver, but
2159      using a receiver filter to only allow revisions to pass through
2160      that are in our rangelist. */
2161   log_target = svn_path_url_add_component2(repos_root, log_target + 1,
2162                                            scratch_pool);
2163
2164   {
2165     svn_error_t *err;
2166
2167     err = logs_for_mergeinfo_rangelist(log_target, merge_source_fspaths,
2168                                        finding_merged,
2169                                        master_inheritable_rangelist,
2170                                        oldest_revs_first,
2171                                        target_mergeinfo_cat,
2172                                        svn_fspath__join("/",
2173                                                         target_repos_relpath,
2174                                                         scratch_pool),
2175                                        discover_changed_paths,
2176                                        revprops,
2177                                        log_receiver, log_receiver_baton,
2178                                        ctx, target_session, scratch_pool);
2179
2180     /* Close the source and target sessions. */
2181     svn_pool_destroy(subpool); /* For SVN_ERR_CEASE_INVOCATION */
2182
2183     return svn_error_trace(err);
2184   }
2185 }
2186
2187 svn_error_t *
2188 svn_client_mergeinfo_log2(svn_boolean_t finding_merged,
2189                           const char *target_path_or_url,
2190                           const svn_opt_revision_t *target_peg_revision,
2191                           const char *source_path_or_url,
2192                           const svn_opt_revision_t *source_peg_revision,
2193                           const svn_opt_revision_t *source_start_revision,
2194                           const svn_opt_revision_t *source_end_revision,
2195                           svn_log_entry_receiver_t log_receiver,
2196                           void *log_receiver_baton,
2197                           svn_boolean_t discover_changed_paths,
2198                           svn_depth_t depth,
2199                           const apr_array_header_t *revprops,
2200                           svn_client_ctx_t *ctx,
2201                           apr_pool_t *scratch_pool)
2202 {
2203   return svn_error_trace(
2204          svn_client__mergeinfo_log(finding_merged, target_path_or_url,
2205                                    target_peg_revision, NULL,
2206                                    source_path_or_url, source_peg_revision,
2207                                    source_start_revision, source_end_revision,
2208                                    log_receiver, log_receiver_baton,
2209                                    discover_changed_paths, depth, revprops,
2210                                    ctx, NULL,
2211                                    scratch_pool, scratch_pool));
2212 }
2213
2214 svn_error_t *
2215 svn_client_suggest_merge_sources(apr_array_header_t **suggestions,
2216                                  const char *path_or_url,
2217                                  const svn_opt_revision_t *peg_revision,
2218                                  svn_client_ctx_t *ctx,
2219                                  apr_pool_t *pool)
2220 {
2221   const char *repos_root;
2222   const char *copyfrom_path;
2223   apr_array_header_t *list;
2224   svn_revnum_t copyfrom_rev;
2225   svn_mergeinfo_catalog_t mergeinfo_cat;
2226   svn_mergeinfo_t mergeinfo;
2227   apr_hash_index_t *hi;
2228   apr_pool_t *session_pool = svn_pool_create(pool);
2229   svn_ra_session_t *ra_session;
2230
2231   list = apr_array_make(pool, 1, sizeof(const char *));
2232
2233   /* In our ideal algorithm, the list of recommendations should be
2234      ordered by:
2235
2236         1. The most recent existing merge source.
2237         2. The copyfrom source (which will also be listed as a merge
2238            source if the copy was made with a 1.5+ client and server).
2239         3. All other merge sources, most recent to least recent.
2240
2241      However, determining the order of application of merge sources
2242      requires a new RA API.  Until such an API is available, our
2243      algorithm will be:
2244
2245         1. The copyfrom source.
2246         2. All remaining merge sources (unordered).
2247   */
2248   SVN_ERR(svn_client__ra_session_from_path2(&ra_session, NULL, path_or_url,
2249                                             NULL, peg_revision, peg_revision,
2250                                             ctx, session_pool));
2251
2252   SVN_ERR(get_mergeinfo(&mergeinfo_cat, &repos_root, path_or_url,
2253                         peg_revision, FALSE, FALSE,
2254                         ctx, ra_session, session_pool, session_pool));
2255
2256   if (mergeinfo_cat && apr_hash_count(mergeinfo_cat))
2257     {
2258       /* We asked only for the PATH_OR_URL's mergeinfo, not any of its
2259          descendants.  So if there is anything in the catalog it is the
2260          mergeinfo for PATH_OR_URL. */
2261       mergeinfo = apr_hash_this_val(apr_hash_first(session_pool,
2262                                                    mergeinfo_cat));
2263     }
2264   else
2265     {
2266       mergeinfo = NULL;
2267     }
2268
2269   /* ### Should we only add the last source or all copy sources back to
2270          the origin? */
2271   SVN_ERR(svn_client__get_copy_source(&copyfrom_path, &copyfrom_rev,
2272                                       path_or_url, peg_revision, ra_session,
2273                                       ctx, session_pool, session_pool));
2274   if (copyfrom_path)
2275     {
2276       APR_ARRAY_PUSH(list, const char *) =
2277         svn_path_url_add_component2(repos_root, copyfrom_path, pool);
2278     }
2279
2280   if (mergeinfo)
2281     {
2282       for (hi = apr_hash_first(session_pool, mergeinfo);
2283            hi;
2284            hi = apr_hash_next(hi))
2285         {
2286           const char *rel_path = apr_hash_this_key(hi);
2287
2288           if (copyfrom_path == NULL || strcmp(rel_path, copyfrom_path) != 0)
2289             APR_ARRAY_PUSH(list, const char *) = \
2290               svn_path_url_add_component2(repos_root, rel_path + 1, pool);
2291         }
2292     }
2293
2294   svn_pool_destroy(session_pool);
2295
2296   *suggestions = list;
2297   return SVN_NO_ERROR;
2298 }
2299
2300 svn_error_t *
2301 svn_client__mergeinfo_status(svn_boolean_t *mergeinfo_changes,
2302                              svn_wc_context_t *wc_ctx,
2303                              const char *local_abspath,
2304                              apr_pool_t *scratch_pool)
2305 {
2306   apr_array_header_t *propchanges;
2307   int i;
2308
2309   *mergeinfo_changes = FALSE;
2310
2311   SVN_ERR(svn_wc_get_prop_diffs2(&propchanges, NULL, wc_ctx,
2312                                  local_abspath, scratch_pool, scratch_pool));
2313
2314   for (i = 0; i < propchanges->nelts; i++)
2315     {
2316       svn_prop_t prop = APR_ARRAY_IDX(propchanges, i, svn_prop_t);
2317       if (strcmp(prop.name, SVN_PROP_MERGEINFO) == 0)
2318         {
2319           *mergeinfo_changes = TRUE;
2320           break;
2321         }
2322     }
2323
2324   return SVN_NO_ERROR;
2325 }