#include <apr_pools.h>
#include <apr_strings.h>
#include "svn_pools.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_string.h"
#include "svn_opt.h"
#include "svn_error.h"
#include "svn_error_codes.h"
#include "svn_props.h"
#include "svn_mergeinfo.h"
#include "svn_sorts.h"
#include "svn_ra.h"
#include "svn_client.h"
#include "svn_hash.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_wc_private.h"
#include "private/svn_ra_private.h"
#include "private/svn_fspath.h"
#include "client.h"
#include "mergeinfo.h"
#include "svn_private_config.h"
svn_client__merge_path_t *
svn_client__merge_path_dup(const svn_client__merge_path_t *old,
apr_pool_t *pool)
{
svn_client__merge_path_t *new = apr_pmemdup(pool, old, sizeof(*old));
new->abspath = apr_pstrdup(pool, old->abspath);
if (new->remaining_ranges)
new->remaining_ranges = svn_rangelist_dup(old->remaining_ranges, pool);
if (new->pre_merge_mergeinfo)
new->pre_merge_mergeinfo = svn_mergeinfo_dup(old->pre_merge_mergeinfo,
pool);
if (new->implicit_mergeinfo)
new->implicit_mergeinfo = svn_mergeinfo_dup(old->implicit_mergeinfo,
pool);
return new;
}
svn_error_t *
svn_client__parse_mergeinfo(svn_mergeinfo_t *mergeinfo,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const svn_string_t *propval;
*mergeinfo = NULL;
SVN_ERR(svn_wc_prop_get2(&propval, wc_ctx, local_abspath, SVN_PROP_MERGEINFO,
scratch_pool, scratch_pool));
if (propval)
SVN_ERR(svn_mergeinfo_parse(mergeinfo, propval->data, result_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__record_wc_mergeinfo(const char *local_abspath,
svn_mergeinfo_t mergeinfo,
svn_boolean_t do_notification,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
svn_string_t *mergeinfo_str = NULL;
svn_boolean_t mergeinfo_changes = FALSE;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
if (mergeinfo)
SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_str, mergeinfo, scratch_pool));
if (do_notification && ctx->notify_func2)
SVN_ERR(svn_client__mergeinfo_status(&mergeinfo_changes, ctx->wc_ctx,
local_abspath, scratch_pool));
SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath, SVN_PROP_MERGEINFO,
mergeinfo_str, svn_depth_empty,
TRUE , NULL,
NULL, NULL ,
NULL, NULL ,
scratch_pool));
if (do_notification && ctx->notify_func2)
{
svn_wc_notify_t *notify =
svn_wc_create_notify(local_abspath,
svn_wc_notify_merge_record_info,
scratch_pool);
if (mergeinfo_changes)
notify->prop_state = svn_wc_notify_state_merged;
else
notify->prop_state = svn_wc_notify_state_changed;
ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__adjust_mergeinfo_source_paths(svn_mergeinfo_t adjusted_mergeinfo,
const char *rel_path,
svn_mergeinfo_t mergeinfo,
apr_pool_t *pool)
{
apr_hash_index_t *hi;
const char *path;
apr_array_header_t *copied_rangelist;
SVN_ERR_ASSERT(adjusted_mergeinfo);
SVN_ERR_ASSERT(mergeinfo);
for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
{
const char *merge_source = svn__apr_hash_index_key(hi);
apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
path = svn_fspath__join(merge_source, rel_path, pool);
copied_rangelist = svn_rangelist_dup(rangelist, pool);
apr_hash_set(adjusted_mergeinfo, path, APR_HASH_KEY_STRING,
copied_rangelist);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__get_wc_mergeinfo(svn_mergeinfo_t *mergeinfo,
svn_boolean_t *inherited,
svn_mergeinfo_inheritance_t inherit,
const char *local_abspath,
const char *limit_abspath,
const char **walked_path,
svn_boolean_t ignore_invalid_mergeinfo,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *walk_relpath = "";
svn_mergeinfo_t wc_mergeinfo;
svn_revnum_t base_revision;
apr_pool_t *iterpool;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
if (limit_abspath)
SVN_ERR_ASSERT(svn_dirent_is_absolute(limit_abspath));
SVN_ERR(svn_wc__node_get_base_rev(&base_revision, ctx->wc_ctx,
local_abspath, scratch_pool));
iterpool = svn_pool_create(scratch_pool);
while (TRUE)
{
svn_pool_clear(iterpool);
if (inherit == svn_mergeinfo_nearest_ancestor)
{
wc_mergeinfo = NULL;
inherit = svn_mergeinfo_inherited;
}
else
{
svn_error_t *err = svn_client__parse_mergeinfo(&wc_mergeinfo,
ctx->wc_ctx,
local_abspath,
result_pool,
iterpool);
if ((ignore_invalid_mergeinfo || walk_relpath [0] != '\0')
&& err
&& err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
svn_error_clear(err);
wc_mergeinfo = apr_hash_make(result_pool);
break;
}
else
{
SVN_ERR(err);
}
}
if (wc_mergeinfo == NULL &&
inherit != svn_mergeinfo_explicit &&
!svn_dirent_is_root(local_abspath, strlen(local_abspath)))
{
svn_boolean_t is_wc_root;
svn_revnum_t parent_base_rev;
svn_revnum_t parent_changed_rev;
if (limit_abspath && strcmp(limit_abspath, local_abspath) == 0)
break;
SVN_ERR(svn_wc_is_wc_root2(&is_wc_root, ctx->wc_ctx,
local_abspath, iterpool));
if (is_wc_root)
break;
walk_relpath = svn_relpath_join(svn_dirent_basename(local_abspath,
iterpool),
walk_relpath, result_pool);
local_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
SVN_ERR(svn_wc__node_get_base_rev(&parent_base_rev,
ctx->wc_ctx, local_abspath,
scratch_pool));
SVN_ERR(svn_wc__node_get_changed_info(&parent_changed_rev,
NULL, NULL,
ctx->wc_ctx, local_abspath,
scratch_pool,
scratch_pool));
if (SVN_IS_VALID_REVNUM(base_revision)
&& (base_revision < parent_changed_rev
|| parent_base_rev < base_revision))
break;
continue;
}
break;
}
svn_pool_destroy(iterpool);
if (svn_path_is_empty(walk_relpath))
{
*inherited = FALSE;
*mergeinfo = wc_mergeinfo;
}
else
{
if (wc_mergeinfo)
{
*inherited = TRUE;
*mergeinfo = apr_hash_make(result_pool);
SVN_ERR(svn_client__adjust_mergeinfo_source_paths(*mergeinfo,
walk_relpath,
wc_mergeinfo,
result_pool));
}
else
{
*inherited = FALSE;
*mergeinfo = NULL;
}
}
if (walked_path)
*walked_path = walk_relpath;
if (*inherited
&& apr_hash_count(*mergeinfo))
{
SVN_ERR(svn_mergeinfo_inheritable2(mergeinfo, *mergeinfo, NULL,
SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
TRUE, result_pool, scratch_pool));
svn_mergeinfo__remove_empty_rangelists(*mergeinfo, result_pool);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__get_wc_mergeinfo_catalog(svn_mergeinfo_catalog_t *mergeinfo_cat,
svn_boolean_t *inherited,
svn_boolean_t include_descendants,
svn_mergeinfo_inheritance_t inherit,
const char *local_abspath,
const char *limit_path,
const char **walked_path,
svn_boolean_t ignore_invalid_mergeinfo,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *target_repos_rel_path;
svn_mergeinfo_t mergeinfo;
const char *repos_root;
svn_node_kind_t kind;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
*mergeinfo_cat = NULL;
SVN_ERR(svn_wc__node_get_repos_info(&repos_root, NULL,
ctx->wc_ctx, local_abspath,
scratch_pool, scratch_pool));
if (!repos_root)
{
if (walked_path)
*walked_path = "";
*inherited = FALSE;
return SVN_NO_ERROR;
}
SVN_ERR(svn_client__path_relative_to_root(&target_repos_rel_path,
ctx->wc_ctx,
local_abspath,
repos_root, FALSE,
NULL, scratch_pool,
scratch_pool));
SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, inherited, inherit,
local_abspath, limit_path,
walked_path, ignore_invalid_mergeinfo,
ctx, result_pool, scratch_pool));
if (mergeinfo)
{
*mergeinfo_cat = apr_hash_make(result_pool);
apr_hash_set(*mergeinfo_cat,
apr_pstrdup(result_pool, target_repos_rel_path),
APR_HASH_KEY_STRING, mergeinfo);
}
SVN_ERR(svn_wc_read_kind(&kind, ctx->wc_ctx, local_abspath, FALSE,
scratch_pool));
if (kind == svn_node_dir && include_descendants)
{
apr_hash_t *mergeinfo_props;
apr_hash_index_t *hi;
svn_opt_revision_t opt;
opt.kind = svn_opt_revision_unspecified;
SVN_ERR(svn_client_propget4(&mergeinfo_props,
SVN_PROP_MERGEINFO,
local_abspath,
&opt, &opt,
NULL, svn_depth_infinity, NULL,
ctx, scratch_pool, scratch_pool));
for (hi = apr_hash_first(scratch_pool, mergeinfo_props);
hi;
hi = apr_hash_next(hi))
{
const char *key_path = svn__apr_hash_index_key(hi);
svn_string_t *propval = svn__apr_hash_index_val(hi);
svn_mergeinfo_t subtree_mergeinfo;
SVN_ERR(svn_client__path_relative_to_root(&key_path, ctx->wc_ctx,
key_path,
repos_root, FALSE,
NULL, result_pool,
scratch_pool));
SVN_ERR(svn_mergeinfo_parse(&subtree_mergeinfo, propval->data,
result_pool));
if (*mergeinfo_cat == NULL)
*mergeinfo_cat = apr_hash_make(result_pool);
apr_hash_set(*mergeinfo_cat, key_path,
APR_HASH_KEY_STRING, subtree_mergeinfo);
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__get_repos_mergeinfo(svn_ra_session_t *ra_session,
svn_mergeinfo_t *target_mergeinfo,
const char *rel_path,
svn_revnum_t rev,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t squelch_incapable,
apr_pool_t *pool)
{
svn_mergeinfo_catalog_t tgt_mergeinfo_cat;
*target_mergeinfo = NULL;
SVN_ERR(svn_client__get_repos_mergeinfo_catalog(&tgt_mergeinfo_cat,
ra_session,
rel_path, rev, inherit,
squelch_incapable, FALSE,
pool, pool));
if (tgt_mergeinfo_cat && apr_hash_count(tgt_mergeinfo_cat))
{
*target_mergeinfo =
svn__apr_hash_index_val(apr_hash_first(pool, tgt_mergeinfo_cat));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__get_repos_mergeinfo_catalog(
svn_mergeinfo_catalog_t *mergeinfo_cat,
svn_ra_session_t *ra_session,
const char *rel_path,
svn_revnum_t rev,
svn_mergeinfo_inheritance_t inherit,
svn_boolean_t squelch_incapable,
svn_boolean_t include_descendants,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
svn_mergeinfo_t repos_mergeinfo_cat;
apr_array_header_t *rel_paths = apr_array_make(scratch_pool, 1,
sizeof(rel_path));
APR_ARRAY_PUSH(rel_paths, const char *) = rel_path;
err = svn_ra_get_mergeinfo(ra_session, &repos_mergeinfo_cat, rel_paths,
rev, inherit, include_descendants, result_pool);
if (err)
{
if (squelch_incapable && err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
{
svn_error_clear(err);
*mergeinfo_cat = NULL;
return SVN_NO_ERROR;
}
else
return svn_error_trace(err);
}
if (repos_mergeinfo_cat == NULL)
{
*mergeinfo_cat = NULL;
}
else
{
const char *repos_root;
const char *session_url;
SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root, scratch_pool));
SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
if (strcmp(repos_root, session_url) == 0)
{
*mergeinfo_cat = repos_mergeinfo_cat;
}
else
{
apr_hash_index_t *hi;
svn_mergeinfo_catalog_t rekeyed_mergeinfo_cat =
apr_hash_make(result_pool);
for (hi = apr_hash_first(scratch_pool, repos_mergeinfo_cat);
hi;
hi = apr_hash_next(hi))
{
const char *path =
svn_path_url_add_component2(session_url,
svn__apr_hash_index_key(hi),
scratch_pool);
SVN_ERR(svn_ra_get_path_relative_to_root(ra_session, &path,
path,
result_pool));
apr_hash_set(rekeyed_mergeinfo_cat, path, APR_HASH_KEY_STRING,
svn__apr_hash_index_val(hi));
}
*mergeinfo_cat = rekeyed_mergeinfo_cat;
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__get_wc_or_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo,
svn_boolean_t *inherited,
svn_boolean_t *from_repos,
svn_boolean_t repos_only,
svn_mergeinfo_inheritance_t inherit,
svn_ra_session_t *ra_session,
const char *target_wcpath,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_mergeinfo_catalog_t tgt_mergeinfo_cat;
*target_mergeinfo = NULL;
SVN_ERR(svn_client__get_wc_or_repos_mergeinfo_catalog(&tgt_mergeinfo_cat,
inherited, from_repos,
FALSE,
repos_only,
FALSE, inherit,
ra_session,
target_wcpath, ctx,
pool, pool));
if (tgt_mergeinfo_cat && apr_hash_count(tgt_mergeinfo_cat))
{
*target_mergeinfo =
svn__apr_hash_index_val(apr_hash_first(pool, tgt_mergeinfo_cat));
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__get_wc_or_repos_mergeinfo_catalog(
svn_mergeinfo_catalog_t *target_mergeinfo_catalog,
svn_boolean_t *inherited,
svn_boolean_t *from_repos,
svn_boolean_t include_descendants,
svn_boolean_t repos_only,
svn_boolean_t ignore_invalid_mergeinfo,
svn_mergeinfo_inheritance_t inherit,
svn_ra_session_t *ra_session,
const char *target_wcpath,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *url;
svn_revnum_t target_rev;
const char *local_abspath;
const char *repos_root;
const char *repos_relpath;
svn_mergeinfo_catalog_t target_mergeinfo_cat_wc = NULL;
svn_mergeinfo_catalog_t target_mergeinfo_cat_repos = NULL;
SVN_ERR(svn_dirent_get_absolute(&local_abspath, target_wcpath,
scratch_pool));
if (from_repos)
*from_repos = FALSE;
SVN_ERR(svn_wc__node_get_origin(NULL, &target_rev, &repos_relpath,
&repos_root, NULL, NULL,
ctx->wc_ctx, local_abspath, FALSE,
scratch_pool, scratch_pool));
if (repos_relpath)
url = svn_path_url_add_component2(repos_root, repos_relpath, scratch_pool);
else
url = NULL;
if (!repos_only)
{
SVN_ERR(svn_client__get_wc_mergeinfo_catalog(&target_mergeinfo_cat_wc,
inherited,
include_descendants,
inherit,
local_abspath,
NULL, NULL,
ignore_invalid_mergeinfo,
ctx,
result_pool,
scratch_pool));
if (! ((*inherited)
|| (inherit == svn_mergeinfo_explicit)
|| (repos_relpath
&& target_mergeinfo_cat_wc
&& apr_hash_get(target_mergeinfo_cat_wc, repos_relpath,
APR_HASH_KEY_STRING))))
{
repos_only = TRUE;
include_descendants = FALSE;
}
}
if (repos_only)
{
if (url != NULL)
{
apr_hash_t *original_props;
SVN_ERR(svn_wc_get_pristine_props(&original_props,
ctx->wc_ctx, local_abspath,
result_pool, scratch_pool));
if (!apr_hash_get(original_props, SVN_PROP_MERGEINFO,
APR_HASH_KEY_STRING))
{
const char *session_url = NULL;
apr_pool_t *sesspool = NULL;
if (ra_session)
{
SVN_ERR(svn_client__ensure_ra_session_url(&session_url,
ra_session,
url, result_pool));
}
else
{
sesspool = svn_pool_create(scratch_pool);
SVN_ERR(svn_client__open_ra_session_internal(
&ra_session, NULL, url, NULL, NULL, FALSE,
TRUE, ctx, sesspool));
}
SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
&target_mergeinfo_cat_repos, ra_session,
"", target_rev, inherit,
TRUE, include_descendants,
result_pool, scratch_pool));
if (target_mergeinfo_cat_repos
&& apr_hash_get(target_mergeinfo_cat_repos,
repos_relpath,
APR_HASH_KEY_STRING))
{
*inherited = TRUE;
if (from_repos)
*from_repos = TRUE;
}
if (sesspool)
{
svn_pool_destroy(sesspool);
}
else if (session_url)
{
SVN_ERR(svn_ra_reparent(ra_session, session_url,
result_pool));
}
}
}
}
if (target_mergeinfo_cat_wc)
{
*target_mergeinfo_catalog = target_mergeinfo_cat_wc;
if (target_mergeinfo_cat_repos)
SVN_ERR(svn_mergeinfo_catalog_merge(*target_mergeinfo_catalog,
target_mergeinfo_cat_repos,
result_pool, scratch_pool));
}
else if (target_mergeinfo_cat_repos)
{
*target_mergeinfo_catalog = target_mergeinfo_cat_repos;
}
else
{
*target_mergeinfo_catalog = NULL;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__get_history_as_mergeinfo(svn_mergeinfo_t *mergeinfo_p,
svn_boolean_t *has_rev_zero_history,
const char *path_or_url,
const svn_opt_revision_t *peg_revision,
svn_revnum_t range_youngest,
svn_revnum_t range_oldest,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_array_header_t *segments;
svn_revnum_t peg_revnum = SVN_INVALID_REVNUM;
apr_pool_t *sesspool = NULL;
svn_ra_session_t *session = ra_session;
if (session == NULL)
{
svn_opt_revision_t opt_rev;
const char *url;
opt_rev.kind = svn_opt_revision_unspecified;
sesspool = svn_pool_create(pool);
SVN_ERR(svn_client__ra_session_from_path(&session, &peg_revnum, &url,
path_or_url, NULL,
peg_revision, &opt_rev,
ctx, sesspool));
}
else
{
const char *local_abspath;
switch (peg_revision->kind)
{
case svn_opt_revision_head:
case svn_opt_revision_unspecified:
SVN_ERR(svn_ra_get_latest_revnum(session, &peg_revnum, pool));
break;
case svn_opt_revision_number:
peg_revnum = peg_revision->value.number;
break;
case svn_opt_revision_date:
SVN_ERR(svn_ra_get_dated_revision(session, &peg_revnum,
peg_revision->value.date, pool));
break;
default:
if (svn_path_is_url(path_or_url))
return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED,
NULL, NULL);
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
pool));
SVN_ERR(svn_client__get_revision_number(&peg_revnum, NULL,
ctx->wc_ctx,
local_abspath,
session, peg_revision,
pool));
break;
}
}
if (! SVN_IS_VALID_REVNUM(range_youngest))
range_youngest = peg_revnum;
if (! SVN_IS_VALID_REVNUM(range_oldest))
range_oldest = 0;
SVN_ERR(svn_client__repos_location_segments(&segments, session, "",
peg_revnum, range_youngest,
range_oldest, ctx, pool));
if (has_rev_zero_history)
{
*has_rev_zero_history = FALSE;
if (segments->nelts)
{
svn_location_segment_t *oldest_segment =
APR_ARRAY_IDX(segments, 0, svn_location_segment_t *);
if (oldest_segment->range_start == 0)
*has_rev_zero_history = TRUE;
}
}
SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(mergeinfo_p, segments, pool));
if (sesspool)
svn_pool_destroy(sesspool);
return SVN_NO_ERROR;
}
static svn_error_t *
should_elide_mergeinfo(svn_boolean_t *elides,
svn_mergeinfo_t parent_mergeinfo,
svn_mergeinfo_t child_mergeinfo,
const char *path_suffix,
apr_pool_t *pool)
{
if (child_mergeinfo == NULL)
{
*elides = FALSE;
}
else if (apr_hash_count(child_mergeinfo) == 0)
{
*elides = (!parent_mergeinfo || apr_hash_count(parent_mergeinfo) == 0);
}
else if (!parent_mergeinfo || apr_hash_count(parent_mergeinfo) == 0)
{
*elides = FALSE;
}
else
{
svn_mergeinfo_t path_tweaked_parent_mergeinfo;
apr_pool_t *subpool = svn_pool_create(pool);
path_tweaked_parent_mergeinfo = apr_hash_make(subpool);
if (path_suffix)
SVN_ERR(svn_client__adjust_mergeinfo_source_paths(
path_tweaked_parent_mergeinfo,
path_suffix, parent_mergeinfo, subpool));
else
path_tweaked_parent_mergeinfo = parent_mergeinfo;
SVN_ERR(svn_mergeinfo__equals(elides,
path_tweaked_parent_mergeinfo,
child_mergeinfo, TRUE, subpool));
svn_pool_destroy(subpool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
elide_mergeinfo(svn_mergeinfo_t parent_mergeinfo,
svn_mergeinfo_t child_mergeinfo,
const char *local_abspath,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
svn_boolean_t elides;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(should_elide_mergeinfo(&elides,
parent_mergeinfo, child_mergeinfo, NULL,
scratch_pool));
if (elides)
{
SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath, SVN_PROP_MERGEINFO,
NULL, svn_depth_empty, TRUE, NULL,
NULL, NULL ,
NULL, NULL ,
scratch_pool));
if (ctx->notify_func2)
{
svn_wc_notify_t *notify;
notify = svn_wc_create_notify(local_abspath,
svn_wc_notify_merge_elide_info,
scratch_pool);
ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
notify = svn_wc_create_notify(local_abspath,
svn_wc_notify_update_update,
scratch_pool);
notify->prop_state = svn_wc_notify_state_changed;
ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__elide_mergeinfo(const char *target_wcpath,
const char *wc_elision_limit_path,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *target_abspath;
const char *limit_abspath = NULL;
SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_wcpath, pool));
if (wc_elision_limit_path)
SVN_ERR(svn_dirent_get_absolute(&limit_abspath, wc_elision_limit_path,
pool));
if (!limit_abspath
|| strcmp(target_abspath, limit_abspath) != 0)
{
svn_mergeinfo_t target_mergeinfo;
svn_mergeinfo_t mergeinfo = NULL;
svn_boolean_t inherited;
const char *walk_path;
svn_error_t *err;
err = svn_client__get_wc_mergeinfo(&target_mergeinfo, &inherited,
svn_mergeinfo_inherited,
target_abspath,
limit_abspath,
&walk_path, FALSE,
ctx, pool, pool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
svn_error_clear(err);
return SVN_NO_ERROR;
}
else
{
return svn_error_trace(err);
}
}
if (inherited || target_mergeinfo == NULL)
return SVN_NO_ERROR;
err = svn_client__get_wc_mergeinfo(&mergeinfo, &inherited,
svn_mergeinfo_nearest_ancestor,
target_abspath,
limit_abspath,
&walk_path, FALSE, ctx, pool, pool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
svn_error_clear(err);
return SVN_NO_ERROR;
}
else
{
return svn_error_trace(err);
}
}
if (!mergeinfo && !wc_elision_limit_path)
{
err = svn_client__get_wc_or_repos_mergeinfo(
&mergeinfo, &inherited, NULL, TRUE,
svn_mergeinfo_nearest_ancestor,
NULL, target_wcpath, ctx, pool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
svn_error_clear(err);
return SVN_NO_ERROR;
}
else
{
return svn_error_trace(err);
}
}
}
if (!mergeinfo && wc_elision_limit_path)
return SVN_NO_ERROR;
SVN_ERR(elide_mergeinfo(mergeinfo, target_mergeinfo, target_abspath,
ctx, pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
get_mergeinfo(svn_mergeinfo_catalog_t *mergeinfo_catalog,
const char **repos_root,
const char *path_or_url,
const svn_opt_revision_t *peg_revision,
svn_boolean_t include_descendants,
svn_boolean_t ignore_invalid_mergeinfo,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_ra_session_t *ra_session;
svn_revnum_t rev;
const char *local_abspath;
const char *url;
svn_boolean_t use_url = svn_path_is_url(path_or_url);
svn_revnum_t peg_rev;
svn_opt_revision_t opt_rev;
opt_rev.kind = svn_opt_revision_unspecified;
SVN_ERR(svn_client__ra_session_from_path(&ra_session, &peg_rev, &url,
path_or_url, NULL, peg_revision,
&opt_rev, ctx, scratch_pool));
if (!use_url)
{
const char *repos_root_url;
const char *repos_relpath;
const char *origin_url = NULL;
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
scratch_pool));
SVN_ERR(svn_wc__node_get_origin(NULL, &rev, &repos_relpath,
&repos_root_url, NULL, NULL,
ctx->wc_ctx, local_abspath, FALSE,
scratch_pool, scratch_pool));
if(repos_relpath)
origin_url = svn_path_url_add_component2(repos_root_url, repos_relpath,
scratch_pool);
if (!origin_url
|| strcmp(origin_url, url) != 0
|| peg_rev != rev)
{
use_url = TRUE;
}
}
SVN_ERR(svn_ra__assert_mergeinfo_capable_server(ra_session, path_or_url,
scratch_pool));
SVN_ERR(svn_ra_get_repos_root2(ra_session, repos_root, result_pool));
if (use_url)
{
rev = peg_rev;
SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
mergeinfo_catalog, ra_session, "", rev, svn_mergeinfo_inherited,
FALSE, include_descendants,
result_pool, scratch_pool));
}
else
{
svn_boolean_t inherited;
SVN_ERR(svn_client__get_wc_or_repos_mergeinfo_catalog(
mergeinfo_catalog, &inherited, NULL, include_descendants, FALSE,
ignore_invalid_mergeinfo, svn_mergeinfo_inherited,
ra_session, path_or_url, ctx,
result_pool, scratch_pool));
}
return SVN_NO_ERROR;
}
struct elide_mergeinfo_catalog_dir_baton {
const char *inherited_mergeinfo_path;
svn_mergeinfo_t mergeinfo_catalog;
};
static svn_error_t *
elide_mergeinfo_catalog_open_root(void *eb,
svn_revnum_t base_revision,
apr_pool_t *dir_pool,
void **root_baton)
{
struct elide_mergeinfo_catalog_dir_baton *b = apr_pcalloc(dir_pool,
sizeof(*b));
b->mergeinfo_catalog = eb;
*root_baton = b;
return SVN_NO_ERROR;
}
static svn_error_t *
elide_mergeinfo_catalog_open_directory(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *dir_pool,
void **child_baton)
{
struct elide_mergeinfo_catalog_dir_baton *b, *pb = parent_baton;
b = apr_pcalloc(dir_pool, sizeof(*b));
b->mergeinfo_catalog = pb->mergeinfo_catalog;
if (apr_hash_get(b->mergeinfo_catalog, path, APR_HASH_KEY_STRING))
b->inherited_mergeinfo_path = apr_pstrdup(dir_pool, path);
else
b->inherited_mergeinfo_path = pb->inherited_mergeinfo_path;
*child_baton = b;
return SVN_NO_ERROR;
}
struct elide_mergeinfo_catalog_cb_baton {
apr_array_header_t *elidable_paths;
svn_mergeinfo_t mergeinfo_catalog;
apr_pool_t *result_pool;
};
static svn_error_t *
elide_mergeinfo_catalog_cb(void **dir_baton,
void *parent_baton,
void *callback_baton,
const char *path,
apr_pool_t *pool)
{
struct elide_mergeinfo_catalog_cb_baton *cb = callback_baton;
struct elide_mergeinfo_catalog_dir_baton *pb = parent_baton;
const char *path_suffix;
svn_boolean_t elides;
SVN_ERR_ASSERT(pb != NULL);
*dir_baton = NULL;
if (!pb->inherited_mergeinfo_path
|| strcmp(path, "/") == 0)
return SVN_NO_ERROR;
path_suffix = svn_dirent_is_child(pb->inherited_mergeinfo_path,
path, NULL);
SVN_ERR_ASSERT(path_suffix != NULL);
SVN_ERR(should_elide_mergeinfo(&elides,
apr_hash_get(cb->mergeinfo_catalog,
pb->inherited_mergeinfo_path,
APR_HASH_KEY_STRING),
apr_hash_get(cb->mergeinfo_catalog,
path,
APR_HASH_KEY_STRING),
path_suffix,
pool));
if (elides)
APR_ARRAY_PUSH(cb->elidable_paths, const char *) =
apr_pstrdup(cb->result_pool, path);
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__elide_mergeinfo_catalog(svn_mergeinfo_t mergeinfo_catalog,
apr_pool_t *pool)
{
apr_array_header_t *paths;
apr_array_header_t *elidable_paths = apr_array_make(pool, 1,
sizeof(const char *));
svn_delta_editor_t *editor = svn_delta_default_editor(pool);
struct elide_mergeinfo_catalog_cb_baton cb = { 0 };
int i;
cb.elidable_paths = elidable_paths;
cb.mergeinfo_catalog = mergeinfo_catalog;
cb.result_pool = pool;
editor->open_root = elide_mergeinfo_catalog_open_root;
editor->open_directory = elide_mergeinfo_catalog_open_directory;
SVN_ERR(svn_hash_keys(&paths, mergeinfo_catalog, pool));
SVN_ERR(svn_delta_path_driver(editor,
mergeinfo_catalog,
SVN_INVALID_REVNUM,
paths,
elide_mergeinfo_catalog_cb,
&cb,
pool));
for (i = 0; i < elidable_paths->nelts; i++)
{
const char *path = APR_ARRAY_IDX(elidable_paths, i, const char *);
apr_hash_set(mergeinfo_catalog, path, APR_HASH_KEY_STRING, NULL);
}
return SVN_NO_ERROR;
}
static svn_mergeinfo_t
find_nearest_ancestor(const apr_array_header_t *depth_first_catalog_index,
svn_boolean_t *ancestor_is_self,
const char *abs_repos_path)
{
int ancestor_index = -1;
*ancestor_is_self = FALSE;
if (depth_first_catalog_index)
{
int i;
for (i = 0; i < depth_first_catalog_index->nelts; i++)
{
svn_sort__item_t item = APR_ARRAY_IDX(depth_first_catalog_index, i,
svn_sort__item_t);
if (svn_fspath__is_ancestor(item.key, abs_repos_path))
{
ancestor_index = i;
if (strcmp(item.key, abs_repos_path) == 0)
{
*ancestor_is_self = TRUE;
break;
}
}
}
}
if (ancestor_index == -1)
return NULL;
else
return (APR_ARRAY_IDX(depth_first_catalog_index,
ancestor_index,
svn_sort__item_t)).value;
}
struct filter_log_entry_baton_t
{
svn_boolean_t filtering_merged;
const apr_array_header_t *merge_source_paths;
const char *abs_repos_target_path;
svn_mergeinfo_catalog_t target_mergeinfo_catalog;
apr_array_header_t *depth_first_catalog_index;
const apr_array_header_t *rangelist;
svn_log_entry_receiver_t log_receiver;
void *log_receiver_baton;
svn_client_ctx_t *ctx;
};
static svn_error_t *
filter_log_entry_with_rangelist(void *baton,
svn_log_entry_t *log_entry,
apr_pool_t *pool)
{
struct filter_log_entry_baton_t *fleb = baton;
apr_array_header_t *intersection, *this_rangelist;
if (fleb->ctx->cancel_func)
SVN_ERR(fleb->ctx->cancel_func(fleb->ctx->cancel_baton));
if (log_entry->revision == 0)
return SVN_NO_ERROR;
this_rangelist = svn_rangelist__initialize(log_entry->revision - 1,
log_entry->revision,
TRUE, pool);
SVN_ERR(svn_rangelist_intersect(&intersection, fleb->rangelist,
this_rangelist, FALSE, pool));
if (! (intersection && intersection->nelts))
return SVN_NO_ERROR;
SVN_ERR_ASSERT(intersection->nelts == 1);
SVN_ERR(svn_rangelist_intersect(&intersection, fleb->rangelist,
this_rangelist, TRUE, pool));
log_entry->non_inheritable = !intersection->nelts;
if ((log_entry->non_inheritable || !fleb->filtering_merged)
&& log_entry->changed_paths2)
{
int i;
apr_hash_index_t *hi;
svn_boolean_t all_subtrees_have_this_rev = TRUE;
apr_array_header_t *this_rev_rangelist =
svn_rangelist__initialize(log_entry->revision - 1,
log_entry->revision, TRUE, pool);
apr_pool_t *iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, log_entry->changed_paths2);
hi;
hi = apr_hash_next(hi))
{
const char *path = svn__apr_hash_index_key(hi);
svn_log_changed_path2_t *change = svn__apr_hash_index_val(hi);
const char *target_path_affected;
svn_mergeinfo_t nearest_ancestor_mergeinfo;
apr_hash_index_t *hi2;
svn_boolean_t found_this_revision = FALSE;
const char *merge_source_rel_target;
const char *merge_source_path;
svn_boolean_t ancestor_is_self;
svn_pool_clear(iterpool);
for (i = 0; i < fleb->merge_source_paths->nelts; i++)
{
merge_source_path = APR_ARRAY_IDX(fleb->merge_source_paths,
i, const char *);
merge_source_rel_target
= svn_fspath__skip_ancestor(merge_source_path, path);
if (merge_source_rel_target)
{
if (merge_source_rel_target[0] == '\0'
&& (change->action != 'M'))
i = fleb->merge_source_paths->nelts;
break;
}
}
if (i == fleb->merge_source_paths->nelts)
continue;
target_path_affected = svn_fspath__join(fleb->abs_repos_target_path,
merge_source_rel_target,
iterpool);
nearest_ancestor_mergeinfo =
find_nearest_ancestor(fleb->depth_first_catalog_index,
&ancestor_is_self,
target_path_affected);
if (ancestor_is_self
&& (change->action != 'M'))
{
apr_array_header_t *rangelist = apr_hash_get(
nearest_ancestor_mergeinfo, path, APR_HASH_KEY_STRING);
svn_merge_range_t *youngest_range = APR_ARRAY_IDX(
rangelist, rangelist->nelts - 1, svn_merge_range_t *);
if (youngest_range
&& (youngest_range->end > log_entry->revision))
continue;
}
if (nearest_ancestor_mergeinfo)
{
for (hi2 = apr_hash_first(iterpool, nearest_ancestor_mergeinfo);
hi2;
hi2 = apr_hash_next(hi2))
{
const char *mergeinfo_path = svn__apr_hash_index_key(hi2);
apr_array_header_t *rangelist = svn__apr_hash_index_val(hi2);
if (svn_fspath__is_ancestor(merge_source_path,
mergeinfo_path))
{
SVN_ERR(svn_rangelist_intersect(&intersection,
rangelist,
this_rev_rangelist,
FALSE,
iterpool));
if (intersection->nelts)
{
if (ancestor_is_self)
{
found_this_revision = TRUE;
break;
}
else
{
SVN_ERR(svn_rangelist_intersect(
&intersection,
rangelist,
this_rev_rangelist,
TRUE, iterpool));
if (intersection->nelts)
{
found_this_revision = TRUE;
break;
}
}
}
}
}
}
if (!found_this_revision)
{
all_subtrees_have_this_rev = FALSE;
break;
}
}
svn_pool_destroy(iterpool);
if (all_subtrees_have_this_rev)
{
if (fleb->filtering_merged)
log_entry->non_inheritable = FALSE;
else
return SVN_NO_ERROR;
}
}
return fleb->log_receiver(fleb->log_receiver_baton, log_entry, pool);
}
static svn_error_t *
logs_for_mergeinfo_rangelist(const char *source_url,
const apr_array_header_t *merge_source_paths,
svn_boolean_t filtering_merged,
const apr_array_header_t *rangelist,
svn_mergeinfo_t target_mergeinfo_catalog,
const char *abs_repos_target_path,
svn_boolean_t discover_changed_paths,
const apr_array_header_t *revprops,
svn_log_entry_receiver_t log_receiver,
void *log_receiver_baton,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
apr_array_header_t *target;
svn_merge_range_t *oldest_range, *youngest_range;
apr_array_header_t *revision_ranges;
svn_opt_revision_t oldest_rev, youngest_rev;
svn_opt_revision_range_t *range;
struct filter_log_entry_baton_t fleb;
if (! rangelist->nelts)
return SVN_NO_ERROR;
qsort(rangelist->elts, rangelist->nelts,
rangelist->elt_size, svn_sort_compare_ranges);
target = apr_array_make(scratch_pool, 1, sizeof(const char *));
APR_ARRAY_PUSH(target, const char *) = source_url;
youngest_range = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
svn_merge_range_t *);
youngest_rev.kind = svn_opt_revision_number;
youngest_rev.value.number = youngest_range->end;
oldest_range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
oldest_rev.kind = svn_opt_revision_number;
oldest_rev.value.number = oldest_range->start;
if (! target_mergeinfo_catalog)
target_mergeinfo_catalog = apr_hash_make(scratch_pool);
if (apr_hash_count(target_mergeinfo_catalog))
{
apr_hash_index_t *hi;
svn_mergeinfo_catalog_t rekeyed_catalog = apr_hash_make(scratch_pool);
for (hi = apr_hash_first(scratch_pool, target_mergeinfo_catalog);
hi;
hi = apr_hash_next(hi))
{
const char *path = svn__apr_hash_index_key(hi);
if (!svn_dirent_is_absolute(path))
apr_hash_set(rekeyed_catalog,
svn_dirent_join("/", path, scratch_pool),
APR_HASH_KEY_STRING,
svn__apr_hash_index_val(hi));
}
target_mergeinfo_catalog = rekeyed_catalog;
}
fleb.filtering_merged = filtering_merged;
fleb.merge_source_paths = merge_source_paths;
fleb.target_mergeinfo_catalog = target_mergeinfo_catalog;
fleb.depth_first_catalog_index =
svn_sort__hash(target_mergeinfo_catalog,
svn_sort_compare_items_as_paths,
scratch_pool);
fleb.abs_repos_target_path = abs_repos_target_path;
fleb.rangelist = rangelist;
fleb.log_receiver = log_receiver;
fleb.log_receiver_baton = log_receiver_baton;
fleb.ctx = ctx;
revision_ranges = apr_array_make(scratch_pool, 1,
sizeof(svn_opt_revision_range_t *));
range = apr_pcalloc(scratch_pool, sizeof(*range));
range->end = youngest_rev;
range->start = oldest_rev;
APR_ARRAY_PUSH(revision_ranges, svn_opt_revision_range_t *) = range;
SVN_ERR(svn_client_log5(target, &youngest_rev, revision_ranges,
0, discover_changed_paths, FALSE, FALSE, revprops,
filter_log_entry_with_rangelist, &fleb, ctx,
scratch_pool));
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
return SVN_NO_ERROR;
}
svn_error_t *
svn_client_mergeinfo_get_merged(apr_hash_t **mergeinfo_p,
const char *path_or_url,
const svn_opt_revision_t *peg_revision,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *repos_root;
apr_hash_t *full_path_mergeinfo;
svn_mergeinfo_catalog_t mergeinfo_cat;
svn_mergeinfo_t mergeinfo;
SVN_ERR(get_mergeinfo(&mergeinfo_cat, &repos_root, path_or_url,
peg_revision, FALSE, FALSE, ctx, pool, pool));
if (mergeinfo_cat)
{
const char *path_or_url_repos_rel;
if (! svn_path_is_url(path_or_url)
&& ! svn_dirent_is_absolute(path_or_url))
SVN_ERR(svn_dirent_get_absolute(&path_or_url, path_or_url, pool));
SVN_ERR(svn_client__path_relative_to_root(&path_or_url_repos_rel,
ctx->wc_ctx, path_or_url,
repos_root, FALSE, NULL,
pool, pool));
mergeinfo = apr_hash_get(mergeinfo_cat, path_or_url_repos_rel,
APR_HASH_KEY_STRING);
}
else
{
mergeinfo = NULL;
}
*mergeinfo_p = NULL;
if (mergeinfo)
{
apr_hash_index_t *hi;
full_path_mergeinfo = apr_hash_make(pool);
for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
{
const char *key = svn__apr_hash_index_key(hi);
void *val = svn__apr_hash_index_val(hi);
apr_hash_set(full_path_mergeinfo,
svn_path_url_add_component2(repos_root, key + 1, pool),
APR_HASH_KEY_STRING, val);
}
*mergeinfo_p = full_path_mergeinfo;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_client_mergeinfo_log(svn_boolean_t finding_merged,
const char *path_or_url,
const svn_opt_revision_t *peg_revision,
const char *merge_source_path_or_url,
const svn_opt_revision_t *src_peg_revision,
svn_log_entry_receiver_t log_receiver,
void *log_receiver_baton,
svn_boolean_t discover_changed_paths,
svn_depth_t depth,
const apr_array_header_t *revprops,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
const char *log_target = NULL;
const char *repos_root;
const char *path_or_url_repos_rel;
svn_mergeinfo_catalog_t path_or_url_mergeinfo_cat;
apr_hash_t *inheritable_subtree_merges;
svn_mergeinfo_t source_history;
svn_mergeinfo_t path_or_url_history;
apr_array_header_t *master_noninheritable_rangelist;
apr_array_header_t *master_inheritable_rangelist;
apr_array_header_t *merge_source_paths =
apr_array_make(scratch_pool, 1, sizeof(const char *));
apr_hash_index_t *hi_catalog;
apr_hash_index_t *hi;
apr_pool_t *iterpool;
if (depth != svn_depth_infinity && depth != svn_depth_empty)
return svn_error_create(
SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Only depths 'infinity' and 'empty' are currently supported"));
SVN_ERR(get_mergeinfo(&path_or_url_mergeinfo_cat, &repos_root,
path_or_url, peg_revision,
depth == svn_depth_infinity, TRUE,
ctx, scratch_pool, scratch_pool));
if (!svn_path_is_url(path_or_url))
SVN_ERR(svn_dirent_get_absolute(&path_or_url, path_or_url, scratch_pool));
SVN_ERR(svn_client__path_relative_to_root(&path_or_url_repos_rel,
ctx->wc_ctx,
path_or_url,
repos_root,
FALSE, NULL,
scratch_pool,
scratch_pool));
if (!path_or_url_mergeinfo_cat)
{
if (finding_merged)
{
return SVN_NO_ERROR;
}
else
{
path_or_url_mergeinfo_cat = apr_hash_make(scratch_pool);
apr_hash_set(path_or_url_mergeinfo_cat,
path_or_url_repos_rel,
APR_HASH_KEY_STRING,
apr_hash_make(scratch_pool));
}
}
if (!finding_merged)
SVN_ERR(svn_client__get_history_as_mergeinfo(&path_or_url_history, NULL,
path_or_url,
peg_revision,
SVN_INVALID_REVNUM,
SVN_INVALID_REVNUM,
NULL, ctx, scratch_pool));
SVN_ERR(svn_client__get_history_as_mergeinfo(&source_history, NULL,
merge_source_path_or_url,
src_peg_revision,
SVN_INVALID_REVNUM,
SVN_INVALID_REVNUM,
NULL, ctx, scratch_pool));
master_noninheritable_rangelist =
apr_array_make(scratch_pool, 64, sizeof(svn_merge_range_t *));
master_inheritable_rangelist = apr_array_make(scratch_pool, 64,
sizeof(svn_merge_range_t *));
inheritable_subtree_merges = apr_hash_make(scratch_pool);
iterpool = svn_pool_create(scratch_pool);
for (hi_catalog = apr_hash_first(scratch_pool,
path_or_url_mergeinfo_cat);
hi_catalog;
hi_catalog = apr_hash_next(hi_catalog))
{
svn_mergeinfo_t subtree_mergeinfo =
svn__apr_hash_index_val(hi_catalog);
svn_mergeinfo_t subtree_history;
svn_mergeinfo_t subtree_source_history;
svn_mergeinfo_t subtree_inheritable_mergeinfo;
svn_mergeinfo_t subtree_noninheritable_mergeinfo;
svn_mergeinfo_t merged_noninheritable;
svn_mergeinfo_t merged;
const char *subtree_path = svn__apr_hash_index_key(hi_catalog);
svn_boolean_t is_subtree = strcmp(subtree_path,
path_or_url_repos_rel) != 0;
svn_pool_clear(iterpool);
if (is_subtree)
{
const char *subtree_rel_path =
subtree_path + strlen(path_or_url_repos_rel) + 1;
SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
&subtree_source_history, source_history,
subtree_rel_path, scratch_pool, scratch_pool));
if (!finding_merged)
SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
&subtree_history, path_or_url_history,
subtree_rel_path, scratch_pool, scratch_pool));
}
else
{
subtree_source_history = source_history;
if (!finding_merged)
subtree_history = path_or_url_history;
}
if (!finding_merged)
{
svn_mergeinfo_t merged_via_history;
SVN_ERR(svn_mergeinfo_intersect2(&merged_via_history,
subtree_history,
subtree_source_history, TRUE,
scratch_pool, scratch_pool));
SVN_ERR(svn_mergeinfo_merge(subtree_mergeinfo,
merged_via_history,
scratch_pool));
}
SVN_ERR(svn_mergeinfo_inheritable2(&subtree_inheritable_mergeinfo,
subtree_mergeinfo, NULL,
SVN_INVALID_REVNUM,
SVN_INVALID_REVNUM,
TRUE, scratch_pool, iterpool));
SVN_ERR(svn_mergeinfo_inheritable2(&subtree_noninheritable_mergeinfo,
subtree_mergeinfo, NULL,
SVN_INVALID_REVNUM,
SVN_INVALID_REVNUM,
FALSE, scratch_pool, iterpool));
SVN_ERR(svn_mergeinfo_intersect2(&merged_noninheritable,
subtree_noninheritable_mergeinfo,
subtree_source_history, FALSE,
scratch_pool, iterpool));
svn_mergeinfo__set_inheritance(merged_noninheritable, FALSE,
scratch_pool);
if (apr_hash_count(merged_noninheritable))
{
for (hi = apr_hash_first(iterpool, merged_noninheritable);
hi;
hi = apr_hash_next(hi))
{
apr_array_header_t *list = svn__apr_hash_index_val(hi);
SVN_ERR(svn_rangelist_merge(
&master_noninheritable_rangelist,
svn_rangelist_dup(list, scratch_pool),
scratch_pool));
}
}
SVN_ERR(svn_mergeinfo_intersect2(&merged,
subtree_inheritable_mergeinfo,
subtree_source_history, FALSE,
scratch_pool, iterpool));
if (apr_hash_count(merged))
{
apr_array_header_t *subtree_merged_rangelist =
apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
for (hi = apr_hash_first(iterpool, merged);
hi;
hi = apr_hash_next(hi))
{
apr_array_header_t *list = svn__apr_hash_index_val(hi);
SVN_ERR(svn_rangelist_merge(&master_inheritable_rangelist,
svn_rangelist_dup(list,
scratch_pool),
scratch_pool));
SVN_ERR(svn_rangelist_merge(&subtree_merged_rangelist,
svn_rangelist_dup(list,
scratch_pool),
scratch_pool));
}
apr_hash_set(inheritable_subtree_merges,
apr_pstrdup(scratch_pool, subtree_path),
APR_HASH_KEY_STRING, subtree_merged_rangelist);
}
else
{
apr_hash_set(inheritable_subtree_merges,
apr_pstrdup(scratch_pool, subtree_path),
APR_HASH_KEY_STRING,
apr_array_make(scratch_pool, 0,
sizeof(svn_merge_range_t *)));
}
}
if (master_inheritable_rangelist->nelts)
{
for (hi = apr_hash_first(scratch_pool, inheritable_subtree_merges);
hi;
hi = apr_hash_next(hi))
{
apr_array_header_t *deleted_rangelist;
apr_array_header_t *added_rangelist;
apr_array_header_t *subtree_merged_rangelist =
svn__apr_hash_index_val(hi);
svn_pool_clear(iterpool);
SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
master_inheritable_rangelist,
subtree_merged_rangelist, TRUE,
iterpool));
if (deleted_rangelist->nelts)
{
svn_rangelist__set_inheritance(deleted_rangelist, FALSE);
SVN_ERR(svn_rangelist_merge(&master_noninheritable_rangelist,
deleted_rangelist,
scratch_pool));
SVN_ERR(svn_rangelist_remove(&master_inheritable_rangelist,
deleted_rangelist,
master_inheritable_rangelist,
FALSE,
scratch_pool));
}
}
}
if (finding_merged)
{
SVN_ERR(svn_rangelist_merge(&master_inheritable_rangelist,
master_noninheritable_rangelist,
scratch_pool));
}
else
{
apr_array_header_t *source_master_rangelist =
apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
for (hi = apr_hash_first(scratch_pool, source_history);
hi;
hi = apr_hash_next(hi))
{
apr_array_header_t *subtree_merged_rangelist =
svn__apr_hash_index_val(hi);
SVN_ERR(svn_rangelist_merge(&source_master_rangelist,
subtree_merged_rangelist,
iterpool));
}
SVN_ERR(svn_rangelist_remove(&source_master_rangelist,
master_noninheritable_rangelist,
source_master_rangelist,
FALSE, scratch_pool));
SVN_ERR(svn_rangelist_merge(&source_master_rangelist,
master_noninheritable_rangelist,
scratch_pool));
SVN_ERR(svn_rangelist_remove(&master_inheritable_rangelist,
master_inheritable_rangelist,
source_master_rangelist,
TRUE, scratch_pool));
}
if (! master_inheritable_rangelist->nelts)
{
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
else
{
svn_merge_range_t *youngest_range = svn_merge_range_dup(
APR_ARRAY_IDX(master_inheritable_rangelist,
master_inheritable_rangelist->nelts - 1,
svn_merge_range_t *), scratch_pool);
apr_array_header_t *youngest_rangelist =
svn_rangelist__initialize(youngest_range->end - 1,
youngest_range->end,
youngest_range->inheritable,
scratch_pool);;
for (hi = apr_hash_first(scratch_pool, source_history);
hi;
hi = apr_hash_next(hi))
{
const char *key = svn__apr_hash_index_key(hi);
apr_array_header_t *subtree_merged_rangelist =
svn__apr_hash_index_val(hi);
apr_array_header_t *intersecting_rangelist;
svn_pool_clear(iterpool);
SVN_ERR(svn_rangelist_intersect(&intersecting_rangelist,
youngest_rangelist,
subtree_merged_rangelist,
FALSE, iterpool));
APR_ARRAY_PUSH(merge_source_paths, const char *) =
apr_pstrdup(scratch_pool, key);
if (intersecting_rangelist->nelts)
log_target = apr_pstrdup(scratch_pool, key);
}
}
svn_pool_destroy(iterpool);
log_target = svn_path_url_add_component2(repos_root, log_target + 1,
scratch_pool);
SVN_ERR(logs_for_mergeinfo_rangelist(log_target, merge_source_paths,
finding_merged,
master_inheritable_rangelist,
path_or_url_mergeinfo_cat,
svn_dirent_join("/",
path_or_url_repos_rel,
scratch_pool),
discover_changed_paths,
revprops,
log_receiver, log_receiver_baton,
ctx, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_client_suggest_merge_sources(apr_array_header_t **suggestions,
const char *path_or_url,
const svn_opt_revision_t *peg_revision,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *repos_root;
const char *copyfrom_path;
apr_array_header_t *list;
svn_revnum_t copyfrom_rev;
svn_mergeinfo_catalog_t mergeinfo_cat;
svn_mergeinfo_t mergeinfo;
apr_hash_index_t *hi;
list = apr_array_make(pool, 1, sizeof(const char *));
SVN_ERR(get_mergeinfo(&mergeinfo_cat, &repos_root, path_or_url,
peg_revision, FALSE, FALSE, ctx, pool, pool));
if (mergeinfo_cat && apr_hash_count(mergeinfo_cat))
{
mergeinfo = svn__apr_hash_index_val(apr_hash_first(pool, mergeinfo_cat));
}
else
{
mergeinfo = NULL;
}
SVN_ERR(svn_client__get_copy_source(path_or_url, peg_revision,
©from_path, ©from_rev,
ctx, pool));
if (copyfrom_path)
{
APR_ARRAY_PUSH(list, const char *) =
svn_path_url_add_component2(repos_root, copyfrom_path, pool);
}
if (mergeinfo)
{
for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
{
const char *rel_path = svn__apr_hash_index_key(hi);
if (copyfrom_path == NULL || strcmp(rel_path, copyfrom_path) != 0)
APR_ARRAY_PUSH(list, const char *) = \
svn_path_url_add_component2(repos_root, rel_path + 1, pool);
}
}
*suggestions = list;
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__mergeinfo_status(svn_boolean_t *mergeinfo_changes,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
apr_array_header_t *propchanges;
int i;
*mergeinfo_changes = FALSE;
SVN_ERR(svn_wc_get_prop_diffs2(&propchanges, NULL, wc_ctx,
local_abspath, scratch_pool, scratch_pool));
for (i = 0; i < propchanges->nelts; i++)
{
svn_prop_t prop = APR_ARRAY_IDX(propchanges, i, svn_prop_t);
if (strcmp(prop.name, SVN_PROP_MERGEINFO) == 0)
{
*mergeinfo_changes = TRUE;
break;
}
}
return SVN_NO_ERROR;
}