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
54svn_client__merge_path_t *
55svn_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
73svn_client__merge_path_t *
74svn_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
83svn_error_t *
84svn_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
105svn_error_t *
106svn_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
152svn_error_t *
153svn_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
200/*** Retrieving mergeinfo. ***/
201
202svn_error_t *
203svn_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
372svn_error_t *
373svn_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
460svn_error_t *
461svn_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
492svn_error_t *
493svn_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
554svn_error_t *
555svn_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
592svn_error_t *
593svn_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
740svn_error_t *
741svn_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
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. */
802static svn_error_t *
803should_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*/
862static svn_error_t *
863elide_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
907svn_error_t *
908svn_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.  */
1031static svn_error_t *
1032get_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 ***/
1104svn_error_t *
1105svn_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. */
1196static svn_mergeinfo_t
1197find_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. */
1238struct 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. */
1288static svn_error_t *
1289filter_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
1501static svn_error_t *
1502logs_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. */
1592static svn_error_t *
1593mergeinfo_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
1623/*** Public APIs ***/
1624
1625svn_error_t *
1626svn_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
1668svn_error_t *
1669svn_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, iterpool));
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, iterpool));
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, iterpool));
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                                     iterpool);
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
2187svn_error_t *
2188svn_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
2214svn_error_t *
2215svn_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
2300svn_error_t *
2301svn_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}
2326