#include <apr_strings.h>
#include <apr_tables.h>
#include <apr_hash.h>
#include "svn_types.h"
#include "svn_hash.h"
#include "svn_wc.h"
#include "svn_delta.h"
#include "svn_diff.h"
#include "svn_mergeinfo.h"
#include "svn_client.h"
#include "svn_string.h"
#include "svn_error.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_io.h"
#include "svn_utf.h"
#include "svn_pools.h"
#include "svn_config.h"
#include "svn_props.h"
#include "svn_time.h"
#include "svn_sorts.h"
#include "svn_subst.h"
#include "svn_ra.h"
#include "client.h"
#include "mergeinfo.h"
#include "private/svn_wc_private.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_fspath.h"
#include "svn_private_config.h"
typedef struct merge_source_t
{
const char *url1;
svn_revnum_t rev1;
const char *url2;
svn_revnum_t rev2;
} merge_source_t;
typedef struct merge_cmd_baton_t {
svn_boolean_t force;
svn_boolean_t dry_run;
svn_boolean_t record_only;
svn_boolean_t sources_ancestral;
svn_boolean_t same_repos;
svn_boolean_t mergeinfo_capable;
svn_boolean_t ignore_ancestry;
svn_boolean_t target_missing_child;
svn_boolean_t reintegrate_merge;
const char *added_path;
const char *target_abspath;
const char *repos_root_url;
merge_source_t merge_source;
apr_array_header_t *implicit_src_gap;
svn_client_ctx_t *ctx;
svn_boolean_t add_necessitated_merge;
apr_hash_t *dry_run_deletions;
apr_hash_t *dry_run_added;
apr_hash_t *conflicted_paths;
apr_hash_t *paths_with_new_mergeinfo;
apr_hash_t *paths_with_deleted_mergeinfo;
const char *diff3_cmd;
const apr_array_header_t *merge_options;
svn_ra_session_t *ra_session1;
svn_ra_session_t *ra_session2;
svn_boolean_t *use_sleep;
apr_pool_t *pool;
} merge_cmd_baton_t;
#define HONOR_MERGEINFO(merge_b) ((merge_b)->mergeinfo_capable \
&& (merge_b)->sources_ancestral \
&& (merge_b)->same_repos \
&& (! (merge_b)->ignore_ancestry))
#define RECORD_MERGEINFO(merge_b) (HONOR_MERGEINFO(merge_b) \
&& !(merge_b)->dry_run)
static svn_error_t *
check_repos_match(merge_cmd_baton_t *merge_b,
const char *local_abspath,
const char *url,
apr_pool_t *scratch_pool)
{
if (!svn_uri__is_ancestor(merge_b->repos_root_url, url))
return svn_error_createf(
SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Url '%s' of '%s' is not in repository '%s'"),
url, svn_dirent_local_style(local_abspath, scratch_pool),
merge_b->repos_root_url);
return SVN_NO_ERROR;
}
static APR_INLINE svn_boolean_t
dry_run_deleted_p(const merge_cmd_baton_t *merge_b, const char *wcpath)
{
return (merge_b->dry_run &&
apr_hash_get(merge_b->dry_run_deletions, wcpath,
APR_HASH_KEY_STRING) != NULL);
}
static APR_INLINE svn_boolean_t
dry_run_added_p(const merge_cmd_baton_t *merge_b, const char *wcpath)
{
return (merge_b->dry_run &&
apr_hash_get(merge_b->dry_run_added, wcpath,
APR_HASH_KEY_STRING) != NULL);
}
static APR_INLINE svn_boolean_t
is_path_conflicted_by_merge(merge_cmd_baton_t *merge_b)
{
return (merge_b->conflicted_paths &&
apr_hash_count(merge_b->conflicted_paths) > 0);
}
static svn_error_t *
perform_obstruction_check(svn_wc_notify_state_t *obstruction_state,
svn_boolean_t *added,
svn_boolean_t *deleted,
svn_node_kind_t *kind,
const merge_cmd_baton_t *merge_b,
const char *local_abspath,
svn_node_kind_t expected_kind,
apr_pool_t *scratch_pool)
{
svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
svn_node_kind_t wc_kind;
svn_boolean_t check_root;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
*obstruction_state = svn_wc_notify_state_inapplicable;
if (added)
*added = FALSE;
if (deleted)
*deleted = FALSE;
if (kind)
*kind = svn_node_none;
if (merge_b->dry_run)
{
if (dry_run_deleted_p(merge_b, local_abspath))
{
*obstruction_state = svn_wc_notify_state_inapplicable;
if (deleted)
*deleted = TRUE;
if (expected_kind != svn_node_unknown
&& expected_kind != svn_node_none)
*obstruction_state = svn_wc_notify_state_obstructed;
return SVN_NO_ERROR;
}
else if (dry_run_added_p(merge_b, local_abspath))
{
*obstruction_state = svn_wc_notify_state_inapplicable;
if (added)
*added = TRUE;
if (kind)
*kind = svn_node_dir;
return SVN_NO_ERROR;
}
}
if (kind == NULL)
kind = &wc_kind;
check_root = ! strcmp(local_abspath, merge_b->target_abspath);
SVN_ERR(svn_wc__check_for_obstructions(obstruction_state,
kind,
added,
deleted,
NULL,
wc_ctx, local_abspath,
check_root,
scratch_pool));
if (*obstruction_state == svn_wc_notify_state_inapplicable
&& expected_kind != svn_node_unknown
&& *kind != expected_kind)
{
*obstruction_state = svn_wc_notify_state_obstructed;
}
return SVN_NO_ERROR;
}
static svn_error_t *
make_conflict_versions(const svn_wc_conflict_version_t **left,
const svn_wc_conflict_version_t **right,
const char *victim_abspath,
svn_node_kind_t node_kind,
merge_cmd_baton_t *merge_b)
{
const char *src_repos_url;
const char *left_url;
const char *right_url;
SVN_ERR(svn_ra_get_repos_root2(merge_b->ra_session1, &src_repos_url,
merge_b->pool));
{
const char *child = svn_dirent_is_child(merge_b->target_abspath,
victim_abspath,
merge_b->pool);
if (child != NULL)
{
left_url = svn_path_url_add_component2(merge_b->merge_source.url1,
child, merge_b->pool);
right_url = svn_path_url_add_component2(merge_b->merge_source.url2,
child, merge_b->pool);
}
else
{
left_url = merge_b->merge_source.url1;
right_url = merge_b->merge_source.url2;
}
}
*left = svn_wc_conflict_version_create(
src_repos_url,
svn_uri__is_child(src_repos_url, left_url, merge_b->pool),
merge_b->merge_source.rev1, node_kind, merge_b->pool);
*right = svn_wc_conflict_version_create(
src_repos_url,
svn_uri__is_child(src_repos_url, right_url, merge_b->pool),
merge_b->merge_source.rev2, node_kind, merge_b->pool);
return SVN_NO_ERROR;
}
static svn_error_t*
make_tree_conflict(svn_wc_conflict_description2_t **conflict,
merge_cmd_baton_t *merge_b,
const char *victim_abspath,
svn_node_kind_t node_kind,
svn_wc_conflict_action_t action,
svn_wc_conflict_reason_t reason)
{
const svn_wc_conflict_version_t *left;
const svn_wc_conflict_version_t *right;
SVN_ERR(make_conflict_versions(&left, &right, victim_abspath, node_kind,
merge_b));
*conflict = svn_wc_conflict_description_create_tree2(
victim_abspath, node_kind, svn_wc_operation_merge,
left, right, merge_b->pool);
(*conflict)->action = action;
(*conflict)->reason = reason;
return SVN_NO_ERROR;
}
static svn_error_t*
tree_conflict(merge_cmd_baton_t *merge_b,
const char *victim_abspath,
svn_node_kind_t node_kind,
svn_wc_conflict_action_t action,
svn_wc_conflict_reason_t reason)
{
const svn_wc_conflict_description2_t *existing_conflict;
svn_wc_conflict_description2_t *conflict;
if (merge_b->record_only || merge_b->dry_run)
return SVN_NO_ERROR;
SVN_ERR(svn_wc__get_tree_conflict(&existing_conflict, merge_b->ctx->wc_ctx,
victim_abspath, merge_b->pool,
merge_b->pool));
if (existing_conflict == NULL)
{
SVN_ERR(make_tree_conflict(&conflict, merge_b, victim_abspath,
node_kind, action, reason));
SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
merge_b->pool));
if (merge_b->conflicted_paths == NULL)
merge_b->conflicted_paths = apr_hash_make(merge_b->pool);
victim_abspath = apr_pstrdup(merge_b->pool, victim_abspath);
apr_hash_set(merge_b->conflicted_paths, victim_abspath,
APR_HASH_KEY_STRING, victim_abspath);
}
return SVN_NO_ERROR;
}
static svn_error_t*
tree_conflict_on_add(merge_cmd_baton_t *merge_b,
const char *victim_abspath,
svn_node_kind_t node_kind,
svn_wc_conflict_action_t action,
svn_wc_conflict_reason_t reason)
{
const svn_wc_conflict_description2_t *existing_conflict;
svn_wc_conflict_description2_t *conflict;
if (merge_b->record_only || merge_b->dry_run)
return SVN_NO_ERROR;
SVN_ERR(make_tree_conflict(&conflict, merge_b, victim_abspath,
node_kind, action, reason));
SVN_ERR(svn_wc__get_tree_conflict(&existing_conflict, merge_b->ctx->wc_ctx,
victim_abspath, merge_b->pool,
merge_b->pool));
if (existing_conflict == NULL)
{
SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
merge_b->pool));
if (merge_b->conflicted_paths == NULL)
merge_b->conflicted_paths = apr_hash_make(merge_b->pool);
victim_abspath = apr_pstrdup(merge_b->pool, victim_abspath);
apr_hash_set(merge_b->conflicted_paths, victim_abspath,
APR_HASH_KEY_STRING, victim_abspath);
}
else if (existing_conflict->action == svn_wc_conflict_action_delete &&
conflict->action == svn_wc_conflict_action_add)
{
SVN_ERR(svn_wc__del_tree_conflict(merge_b->ctx->wc_ctx,
victim_abspath, merge_b->pool));
conflict->reason = existing_conflict->reason;
conflict->action = svn_wc_conflict_action_replace;
conflict->src_left_version = svn_wc_conflict_version_dup(
existing_conflict->src_left_version,
merge_b->pool);
SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
merge_b->pool));
if (merge_b->conflicted_paths == NULL)
merge_b->conflicted_paths = apr_hash_make(merge_b->pool);
victim_abspath = apr_pstrdup(merge_b->pool, victim_abspath);
apr_hash_set(merge_b->conflicted_paths, victim_abspath,
APR_HASH_KEY_STRING, victim_abspath);
}
return SVN_NO_ERROR;
}
static svn_error_t*
split_mergeinfo_on_revision(svn_mergeinfo_t *younger_mergeinfo,
svn_mergeinfo_t *mergeinfo,
svn_revnum_t revision,
apr_pool_t *pool)
{
apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(pool);
*younger_mergeinfo = NULL;
for (hi = apr_hash_first(pool, *mergeinfo); hi; hi = apr_hash_next(hi))
{
int i;
const char *merge_source_path = svn__apr_hash_index_key(hi);
apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
svn_pool_clear(iterpool);
for (i = 0; i < rangelist->nelts; i++)
{
svn_merge_range_t *range =
APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
if (range->end <= revision)
{
continue;
}
else
{
int j;
apr_array_header_t *younger_rangelist =
apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
for (j = i; j < rangelist->nelts; j++)
{
svn_merge_range_t *younger_range = svn_merge_range_dup(
APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *), pool);
if (j == i && range->start + 1 <= revision)
younger_range->start = range->end = revision;
APR_ARRAY_PUSH(younger_rangelist, svn_merge_range_t *) =
younger_range;
}
if (!(*younger_mergeinfo))
*younger_mergeinfo = apr_hash_make(pool);
apr_hash_set(*younger_mergeinfo,
(const char *)merge_source_path,
APR_HASH_KEY_STRING, younger_rangelist);
SVN_ERR(svn_mergeinfo_remove2(mergeinfo, *younger_mergeinfo,
*mergeinfo, TRUE, pool, iterpool));
break;
}
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
omit_mergeinfo_changes(apr_array_header_t **trimmed_propchanges,
const apr_array_header_t *propchanges,
apr_pool_t *result_pool)
{
int i;
*trimmed_propchanges = apr_array_make(result_pool,
propchanges->nelts,
sizeof(svn_prop_t));
for (i = 0; i < propchanges->nelts; ++i)
{
const svn_prop_t *change = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
if (strcmp(change->name, SVN_PROP_MERGEINFO) != 0)
APR_ARRAY_PUSH(*trimmed_propchanges, svn_prop_t) = *change;
}
return SVN_NO_ERROR;
}
static svn_error_t*
filter_self_referential_mergeinfo(apr_array_header_t **props,
const char *local_abspath,
svn_boolean_t honor_mergeinfo,
svn_boolean_t same_repos,
svn_boolean_t reintegrate_merge,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
apr_array_header_t *adjusted_props;
int i;
apr_pool_t *iterpool;
svn_boolean_t is_added;
svn_revnum_t base_revision;
if (! same_repos)
return svn_error_trace(omit_mergeinfo_changes(props, *props, pool));
if (! honor_mergeinfo
&& ! reintegrate_merge)
return SVN_NO_ERROR;
SVN_ERR(svn_wc__node_is_added(&is_added, ctx->wc_ctx, local_abspath, pool));
if (is_added)
return SVN_NO_ERROR;
SVN_ERR(svn_wc__node_get_base_rev(&base_revision, ctx->wc_ctx,
local_abspath, pool));
adjusted_props = apr_array_make(pool, (*props)->nelts, sizeof(svn_prop_t));
iterpool = svn_pool_create(pool);
for (i = 0; i < (*props)->nelts; ++i)
{
svn_prop_t *prop = &APR_ARRAY_IDX((*props), i, svn_prop_t);
svn_mergeinfo_t mergeinfo, younger_mergeinfo;
svn_mergeinfo_t filtered_mergeinfo = NULL;
svn_mergeinfo_t filtered_younger_mergeinfo = NULL;
const char *target_url;
const char *old_url = NULL;
svn_error_t *err;
if ((strcmp(prop->name, SVN_PROP_MERGEINFO) != 0)
|| (! prop->value)
|| (! prop->value->len))
{
APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
continue;
}
svn_pool_clear(iterpool);
SVN_ERR(svn_client_url_from_path2(&target_url, local_abspath,
ctx, iterpool, iterpool));
SVN_ERR(svn_client__ensure_ra_session_url(&old_url,
ra_session,
target_url, iterpool));
err = svn_mergeinfo_parse(&mergeinfo, prop->value->data, iterpool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
svn_error_clear(err);
APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
if (old_url)
SVN_ERR(svn_ra_reparent(ra_session, old_url, iterpool));
continue;
}
else
{
return svn_error_trace(err);
}
}
SVN_ERR(split_mergeinfo_on_revision(&younger_mergeinfo,
&mergeinfo,
base_revision,
iterpool));
if (younger_mergeinfo)
{
apr_hash_index_t *hi;
const char *merge_source_root_url;
SVN_ERR(svn_ra_get_repos_root2(ra_session,
&merge_source_root_url, iterpool));
for (hi = apr_hash_first(iterpool, younger_mergeinfo);
hi; hi = apr_hash_next(hi))
{
int j;
const char *source_path = svn__apr_hash_index_key(hi);
apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
const char *merge_source_url;
apr_array_header_t *adjusted_rangelist =
apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
merge_source_url =
svn_path_url_add_component2(merge_source_root_url,
source_path + 1, iterpool);
for (j = 0; j < rangelist->nelts; j++)
{
svn_error_t *err2;
svn_opt_revision_t *start_revision;
const char *start_url;
svn_opt_revision_t peg_rev, rev1_opt, rev2_opt;
svn_merge_range_t *range =
APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *);
peg_rev.kind = svn_opt_revision_number;
peg_rev.value.number = base_revision;
rev1_opt.kind = svn_opt_revision_number;
rev1_opt.value.number = range->start + 1;
rev2_opt.kind = svn_opt_revision_unspecified;
err2 = svn_client__repos_locations(&start_url,
&start_revision,
NULL,
NULL,
ra_session,
target_url,
&peg_rev,
&rev1_opt,
&rev2_opt,
ctx,
iterpool);
if (err2)
{
if (err2->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES
|| err2->apr_err == SVN_ERR_FS_NOT_FOUND
|| err2->apr_err == SVN_ERR_FS_NO_SUCH_REVISION)
{
svn_error_clear(err2);
err2 = NULL;
APR_ARRAY_PUSH(adjusted_rangelist,
svn_merge_range_t *) = range;
}
else
{
return svn_error_trace(err2);
}
}
else
{
if (strcmp(start_url, merge_source_url) != 0)
{
APR_ARRAY_PUSH(adjusted_rangelist,
svn_merge_range_t *) = range;
}
}
}
if (adjusted_rangelist->nelts)
{
if (!filtered_younger_mergeinfo)
filtered_younger_mergeinfo = apr_hash_make(iterpool);
apr_hash_set(filtered_younger_mergeinfo, source_path,
APR_HASH_KEY_STRING, adjusted_rangelist);
}
}
}
if (mergeinfo)
{
svn_mergeinfo_t implicit_mergeinfo;
svn_opt_revision_t peg_rev;
peg_rev.kind = svn_opt_revision_number;
peg_rev.value.number = base_revision;
SVN_ERR(svn_client__get_history_as_mergeinfo(
&implicit_mergeinfo, NULL,
local_abspath, &peg_rev,
base_revision,
SVN_INVALID_REVNUM,
ra_session,
ctx,
iterpool));
SVN_ERR(svn_mergeinfo_remove2(&filtered_mergeinfo,
implicit_mergeinfo,
mergeinfo, TRUE, iterpool, iterpool));
}
if (old_url)
SVN_ERR(svn_ra_reparent(ra_session, old_url, iterpool));
if (filtered_mergeinfo && filtered_younger_mergeinfo)
SVN_ERR(svn_mergeinfo_merge(filtered_mergeinfo,
filtered_younger_mergeinfo, iterpool));
else if (filtered_younger_mergeinfo)
filtered_mergeinfo = filtered_younger_mergeinfo;
if (filtered_mergeinfo && apr_hash_count(filtered_mergeinfo))
{
svn_string_t *filtered_mergeinfo_str;
svn_prop_t *adjusted_prop = apr_pcalloc(pool,
sizeof(*adjusted_prop));
SVN_ERR(svn_mergeinfo_to_string(&filtered_mergeinfo_str,
filtered_mergeinfo,
pool));
adjusted_prop->name = SVN_PROP_MERGEINFO;
adjusted_prop->value = filtered_mergeinfo_str;
APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *adjusted_prop;
}
}
svn_pool_destroy(iterpool);
*props = adjusted_props;
return SVN_NO_ERROR;
}
static svn_error_t *
merge_props_changed(svn_wc_notify_state_t *state,
svn_boolean_t *tree_conflicted,
const char *local_abspath,
const apr_array_header_t *propchanges,
apr_hash_t *original_props,
void *baton,
apr_pool_t *scratch_pool)
{
apr_array_header_t *props;
merge_cmd_baton_t *merge_b = baton;
svn_client_ctx_t *ctx = merge_b->ctx;
svn_error_t *err;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
scratch_pool));
if (merge_b->record_only && props->nelts)
{
apr_array_header_t *mergeinfo_props =
apr_array_make(scratch_pool, 1, sizeof(svn_prop_t));
int i;
for (i = 0; i < props->nelts; i++)
{
svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
{
APR_ARRAY_PUSH(mergeinfo_props, svn_prop_t) = *prop;
break;
}
}
props = mergeinfo_props;
}
if (props->nelts)
{
if (merge_b->merge_source.rev1 < merge_b->merge_source.rev2
|| !merge_b->sources_ancestral)
SVN_ERR(filter_self_referential_mergeinfo(&props,
local_abspath,
HONOR_MERGEINFO(merge_b),
merge_b->same_repos,
merge_b->reintegrate_merge,
merge_b->ra_session2,
merge_b->ctx,
scratch_pool));
err = svn_wc_merge_props3(state, ctx->wc_ctx, local_abspath, NULL, NULL,
original_props, props, merge_b->dry_run,
ctx->conflict_func2, ctx->conflict_baton2,
ctx->cancel_func, ctx->cancel_baton,
scratch_pool);
if (!merge_b->dry_run)
{
int i;
for (i = 0; i < props->nelts; ++i)
{
svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
{
svn_boolean_t has_pristine_mergeinfo = FALSE;
apr_hash_t *pristine_props;
SVN_ERR(svn_wc_get_pristine_props(&pristine_props,
ctx->wc_ctx,
local_abspath,
scratch_pool,
scratch_pool));
if (pristine_props
&& apr_hash_get(pristine_props, SVN_PROP_MERGEINFO,
APR_HASH_KEY_STRING))
has_pristine_mergeinfo = TRUE;
if (!has_pristine_mergeinfo && prop->value)
{
if (!merge_b->paths_with_new_mergeinfo)
merge_b->paths_with_new_mergeinfo =
apr_hash_make(merge_b->pool);
apr_hash_set(merge_b->paths_with_new_mergeinfo,
apr_pstrdup(merge_b->pool, local_abspath),
APR_HASH_KEY_STRING, local_abspath);
}
else if (has_pristine_mergeinfo && !prop->value)
{
if (!merge_b->paths_with_deleted_mergeinfo)
merge_b->paths_with_deleted_mergeinfo =
apr_hash_make(merge_b->pool);
apr_hash_set(merge_b->paths_with_deleted_mergeinfo,
apr_pstrdup(merge_b->pool, local_abspath),
APR_HASH_KEY_STRING, local_abspath);
}
}
}
}
if (err && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND
|| err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS))
{
if (state)
*state = svn_wc_notify_state_missing;
if (tree_conflicted)
*tree_conflicted = TRUE;
svn_error_clear(err);
return SVN_NO_ERROR;
}
else if (err)
return svn_error_trace(err);
}
return SVN_NO_ERROR;
}
static svn_error_t *
merge_dir_props_changed(svn_wc_notify_state_t *state,
svn_boolean_t *tree_conflicted,
const char *local_abspath,
svn_boolean_t dir_was_added,
const apr_array_header_t *propchanges,
apr_hash_t *original_props,
void *diff_baton,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = diff_baton;
svn_wc_notify_state_t obstr_state;
SVN_ERR(perform_obstruction_check(&obstr_state, NULL, NULL,
NULL,
merge_b, local_abspath, svn_node_dir,
scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
{
if (state)
*state = obstr_state;
return SVN_NO_ERROR;
}
if (dir_was_added
&& merge_b->dry_run
&& dry_run_added_p(merge_b, local_abspath))
{
return SVN_NO_ERROR;
}
return svn_error_trace(merge_props_changed(state,
tree_conflicted,
local_abspath,
propchanges,
original_props,
diff_baton,
scratch_pool));
}
typedef struct conflict_resolver_baton_t
{
svn_wc_conflict_resolver_func2_t wrapped_func;
void *wrapped_baton;
apr_hash_t **conflicted_paths;
apr_pool_t *pool;
} conflict_resolver_baton_t;
static svn_error_t *
conflict_resolver(svn_wc_conflict_result_t **result,
const svn_wc_conflict_description2_t *description,
void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
conflict_resolver_baton_t *conflict_b = baton;
if (conflict_b->wrapped_func)
err = (*conflict_b->wrapped_func)(result, description,
conflict_b->wrapped_baton,
result_pool,
scratch_pool);
else
{
*result = svn_wc_create_conflict_result(svn_wc_conflict_choose_postpone,
NULL, result_pool);
err = SVN_NO_ERROR;
}
if ((! conflict_b->wrapped_func)
|| (*result && ((*result)->choice == svn_wc_conflict_choose_postpone)))
{
const char *conflicted_path = apr_pstrdup(conflict_b->pool,
description->local_abspath);
if (*conflict_b->conflicted_paths == NULL)
*conflict_b->conflicted_paths = apr_hash_make(conflict_b->pool);
apr_hash_set(*conflict_b->conflicted_paths, conflicted_path,
APR_HASH_KEY_STRING, conflicted_path);
}
return svn_error_trace(err);
}
static svn_error_t *
merge_file_opened(svn_boolean_t *tree_conflicted,
svn_boolean_t *skip,
const char *path,
svn_revnum_t rev,
void *diff_baton,
apr_pool_t *scratch_pool)
{
return SVN_NO_ERROR;
}
static svn_error_t *
merge_file_changed(svn_wc_notify_state_t *content_state,
svn_wc_notify_state_t *prop_state,
svn_boolean_t *tree_conflicted,
const char *mine_abspath,
const char *older_abspath,
const char *yours_abspath,
svn_revnum_t older_rev,
svn_revnum_t yours_rev,
const char *mimetype1,
const char *mimetype2,
const apr_array_header_t *prop_changes,
apr_hash_t *original_props,
void *baton,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = baton;
enum svn_wc_merge_outcome_t merge_outcome;
svn_node_kind_t wc_kind;
svn_boolean_t is_deleted;
SVN_ERR_ASSERT(mine_abspath && svn_dirent_is_absolute(mine_abspath));
SVN_ERR_ASSERT(!older_abspath || svn_dirent_is_absolute(older_abspath));
SVN_ERR_ASSERT(!yours_abspath || svn_dirent_is_absolute(yours_abspath));
{
svn_wc_notify_state_t obstr_state;
SVN_ERR(perform_obstruction_check(&obstr_state, NULL,
&is_deleted, &wc_kind,
merge_b, mine_abspath, svn_node_unknown,
scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
{
if (content_state)
*content_state = obstr_state;
if (prop_state && obstr_state == svn_wc_notify_state_missing)
*prop_state = svn_wc_notify_state_missing;
return SVN_NO_ERROR;
}
}
if (wc_kind != svn_node_file || is_deleted)
{
if (wc_kind == svn_node_none)
{
svn_depth_t parent_depth;
SVN_ERR(svn_wc__node_get_depth(&parent_depth, merge_b->ctx->wc_ctx,
svn_dirent_dirname(mine_abspath,
scratch_pool),
scratch_pool));
if (parent_depth < svn_depth_files
&& parent_depth != svn_depth_unknown)
{
if (content_state)
*content_state = svn_wc_notify_state_missing;
if (prop_state)
*prop_state = svn_wc_notify_state_missing;
return SVN_NO_ERROR;
}
}
SVN_ERR(tree_conflict(merge_b, mine_abspath, svn_node_file,
svn_wc_conflict_action_edit,
svn_wc_conflict_reason_missing));
if (tree_conflicted)
*tree_conflicted = TRUE;
if (content_state)
*content_state = svn_wc_notify_state_missing;
if (prop_state)
*prop_state = svn_wc_notify_state_missing;
return SVN_NO_ERROR;
}
if (prop_changes->nelts > 0)
{
svn_boolean_t tree_conflicted2 = FALSE;
SVN_ERR(merge_props_changed(prop_state, &tree_conflicted2,
mine_abspath, prop_changes, original_props,
baton, scratch_pool));
if (tree_conflicted2)
{
if (tree_conflicted != NULL)
*tree_conflicted = TRUE;
return SVN_NO_ERROR;
}
}
else if (prop_state)
*prop_state = svn_wc_notify_state_unchanged;
if (merge_b->record_only)
{
if (content_state)
*content_state = svn_wc_notify_state_unchanged;
return SVN_NO_ERROR;
}
if (older_abspath)
{
svn_boolean_t has_local_mods;
const char *target_label = _(".working");
const char *left_label = apr_psprintf(scratch_pool,
_(".merge-left.r%ld"),
older_rev);
const char *right_label = apr_psprintf(scratch_pool,
_(".merge-right.r%ld"),
yours_rev);
conflict_resolver_baton_t conflict_baton = { 0 };
const svn_wc_conflict_version_t *left;
const svn_wc_conflict_version_t *right;
SVN_ERR(svn_wc_text_modified_p2(&has_local_mods, merge_b->ctx->wc_ctx,
mine_abspath, FALSE, scratch_pool));
conflict_baton.wrapped_func = merge_b->ctx->conflict_func2;
conflict_baton.wrapped_baton = merge_b->ctx->conflict_baton2;
conflict_baton.conflicted_paths = &merge_b->conflicted_paths;
conflict_baton.pool = merge_b->pool;
SVN_ERR(make_conflict_versions(&left, &right, mine_abspath,
svn_node_file, merge_b));
SVN_ERR(svn_wc_merge4(&merge_outcome, merge_b->ctx->wc_ctx,
older_abspath, yours_abspath, mine_abspath,
left_label, right_label, target_label,
left, right,
merge_b->dry_run, merge_b->diff3_cmd,
merge_b->merge_options, prop_changes,
conflict_resolver, &conflict_baton,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
scratch_pool));
if (content_state)
{
if (merge_outcome == svn_wc_merge_conflict)
*content_state = svn_wc_notify_state_conflicted;
else if (has_local_mods
&& merge_outcome != svn_wc_merge_unchanged)
*content_state = svn_wc_notify_state_merged;
else if (merge_outcome == svn_wc_merge_merged)
*content_state = svn_wc_notify_state_changed;
else if (merge_outcome == svn_wc_merge_no_merge)
*content_state = svn_wc_notify_state_missing;
else
*content_state = svn_wc_notify_state_unchanged;
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
merge_file_added(svn_wc_notify_state_t *content_state,
svn_wc_notify_state_t *prop_state,
svn_boolean_t *tree_conflicted,
const char *mine_abspath,
const char *older_abspath,
const char *yours_abspath,
svn_revnum_t rev1,
svn_revnum_t rev2,
const char *mimetype1,
const char *mimetype2,
const char *copyfrom_path,
svn_revnum_t copyfrom_revision,
const apr_array_header_t *prop_changes,
apr_hash_t *original_props,
void *baton,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = baton;
svn_node_kind_t kind;
int i;
apr_hash_t *file_props;
SVN_ERR_ASSERT(svn_dirent_is_absolute(mine_abspath));
if (merge_b->record_only)
{
if (content_state)
*content_state = svn_wc_notify_state_unchanged;
if (prop_state)
*prop_state = svn_wc_notify_state_unchanged;
return SVN_NO_ERROR;
}
if (prop_state)
*prop_state = svn_wc_notify_state_unknown;
file_props = apr_hash_copy(scratch_pool, original_props);
for (i = 0; i < prop_changes->nelts; ++i)
{
const svn_prop_t *prop = &APR_ARRAY_IDX(prop_changes, i, svn_prop_t);
if (svn_property_kind(NULL, prop->name) == svn_prop_wc_kind)
continue;
if ((! merge_b->same_repos)
&& (svn_property_kind(NULL, prop->name) != svn_prop_regular_kind))
continue;
if ((! merge_b->same_repos)
&& strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
continue;
apr_hash_set(file_props, prop->name, APR_HASH_KEY_STRING, prop->value);
}
{
svn_wc_notify_state_t obstr_state;
SVN_ERR(perform_obstruction_check(&obstr_state, NULL, NULL,
&kind,
merge_b, mine_abspath, svn_node_unknown,
scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
{
if (merge_b->dry_run && merge_b->added_path
&& svn_dirent_is_child(merge_b->added_path, mine_abspath, NULL))
{
if (content_state)
*content_state = svn_wc_notify_state_changed;
if (prop_state && apr_hash_count(file_props))
*prop_state = svn_wc_notify_state_changed;
}
else if (content_state)
*content_state = obstr_state;
return SVN_NO_ERROR;
}
}
SVN_ERR(svn_io_check_path(mine_abspath, &kind, scratch_pool));
switch (kind)
{
case svn_node_none:
{
if (! merge_b->dry_run)
{
const char *copyfrom_url;
svn_revnum_t copyfrom_rev;
svn_stream_t *new_contents, *new_base_contents;
apr_hash_t *new_base_props, *new_props;
const svn_wc_conflict_description2_t *existing_conflict;
if (merge_b->same_repos)
{
const char *child =
svn_dirent_is_child(merge_b->target_abspath, mine_abspath,
scratch_pool);
if (child != NULL)
copyfrom_url = svn_path_url_add_component2(
merge_b->merge_source.url2,
child, scratch_pool);
else
copyfrom_url = merge_b->merge_source.url2;
copyfrom_rev = rev2;
SVN_ERR(check_repos_match(merge_b, mine_abspath, copyfrom_url,
scratch_pool));
new_base_props = file_props;
new_props = NULL;
SVN_ERR(svn_stream_open_readonly(&new_base_contents,
yours_abspath,
scratch_pool,
scratch_pool));
new_contents = NULL;
}
else
{
copyfrom_url = NULL;
copyfrom_rev = SVN_INVALID_REVNUM;
new_base_props = apr_hash_make(scratch_pool);
new_props = file_props;
new_base_contents = svn_stream_empty(scratch_pool);
SVN_ERR(svn_stream_open_readonly(&new_contents, yours_abspath,
scratch_pool, scratch_pool));
}
SVN_ERR(svn_wc__get_tree_conflict(&existing_conflict,
merge_b->ctx->wc_ctx,
mine_abspath, merge_b->pool,
merge_b->pool));
if (existing_conflict)
{
SVN_ERR(tree_conflict_on_add(merge_b, mine_abspath,
svn_node_file,
svn_wc_conflict_action_add,
svn_wc_conflict_reason_added));
if (tree_conflicted)
*tree_conflicted = TRUE;
}
else
{
SVN_ERR(svn_wc_add_repos_file4(merge_b->ctx->wc_ctx,
mine_abspath,
new_base_contents,
new_contents,
new_base_props, new_props,
copyfrom_url, copyfrom_rev,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
scratch_pool));
}
}
if (content_state)
*content_state = svn_wc_notify_state_changed;
if (prop_state && apr_hash_count(file_props))
*prop_state = svn_wc_notify_state_changed;
}
break;
case svn_node_dir:
SVN_ERR(tree_conflict_on_add(merge_b, mine_abspath, svn_node_file,
svn_wc_conflict_action_add,
svn_wc_conflict_reason_obstructed));
if (tree_conflicted)
*tree_conflicted = TRUE;
if (content_state)
{
svn_node_kind_t wc_kind;
SVN_ERR(svn_wc_read_kind(&wc_kind, merge_b->ctx->wc_ctx,
mine_abspath, FALSE, scratch_pool));
if ((wc_kind != svn_node_none)
&& dry_run_deleted_p(merge_b, mine_abspath))
*content_state = svn_wc_notify_state_changed;
else
*content_state = svn_wc_notify_state_obstructed;
}
break;
case svn_node_file:
{
if (dry_run_deleted_p(merge_b, mine_abspath))
{
if (content_state)
*content_state = svn_wc_notify_state_changed;
}
else
{
SVN_ERR(tree_conflict_on_add(
merge_b, mine_abspath, svn_node_file,
svn_wc_conflict_action_add,
svn_wc_conflict_reason_added));
if (tree_conflicted)
*tree_conflicted = TRUE;
}
break;
}
default:
if (content_state)
*content_state = svn_wc_notify_state_unknown;
break;
}
return SVN_NO_ERROR;
}
static svn_error_t *
properties_same_p(svn_boolean_t *same,
apr_hash_t *props1,
apr_hash_t *props2,
apr_pool_t *scratch_pool)
{
apr_array_header_t *prop_changes;
int i, diffs;
SVN_ERR(svn_prop_diffs(&prop_changes, props1, props2, scratch_pool));
diffs = 0;
for (i = 0; i < prop_changes->nelts; i++)
{
const char *pname = APR_ARRAY_IDX(prop_changes, i, svn_prop_t).name;
if (svn_wc_is_normal_prop(pname)
&& strcmp(pname, SVN_PROP_MERGEINFO) != 0)
diffs++;
}
*same = (diffs == 0);
return SVN_NO_ERROR;
}
static svn_error_t *
files_same_p(svn_boolean_t *same,
const char *older_abspath,
apr_hash_t *original_props,
const char *mine_abspath,
svn_wc_context_t *wc_ctx,
apr_pool_t *scratch_pool)
{
apr_hash_t *working_props;
SVN_ERR(svn_wc_prop_list2(&working_props, wc_ctx, mine_abspath,
scratch_pool, scratch_pool));
SVN_ERR(properties_same_p(same, original_props, working_props,
scratch_pool));
if (*same)
{
svn_stream_t *mine_stream;
svn_stream_t *older_stream;
svn_opt_revision_t working_rev;
working_rev.kind = svn_opt_revision_working;
if (svn_prop_get_value(working_props, SVN_PROP_SPECIAL) != NULL)
SVN_ERR(svn_subst_read_specialfile(&mine_stream, mine_abspath,
scratch_pool, scratch_pool));
else
SVN_ERR(svn_client__get_normalized_stream(&mine_stream, wc_ctx,
mine_abspath, &working_rev,
FALSE, TRUE, NULL, NULL,
scratch_pool, scratch_pool));
SVN_ERR(svn_stream_open_readonly(&older_stream, older_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_stream_contents_same2(same, mine_stream, older_stream,
scratch_pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
merge_file_deleted(svn_wc_notify_state_t *state,
svn_boolean_t *tree_conflicted,
const char *mine_abspath,
const char *older_abspath,
const char *yours_abspath,
const char *mimetype1,
const char *mimetype2,
apr_hash_t *original_props,
void *baton,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = baton;
svn_node_kind_t kind;
if (merge_b->dry_run)
{
const char *wcpath = apr_pstrdup(merge_b->pool, mine_abspath);
apr_hash_set(merge_b->dry_run_deletions, wcpath,
APR_HASH_KEY_STRING, wcpath);
}
if (merge_b->record_only)
{
if (state)
*state = svn_wc_notify_state_unchanged;
return SVN_NO_ERROR;
}
{
svn_wc_notify_state_t obstr_state;
SVN_ERR(perform_obstruction_check(&obstr_state, NULL, NULL,
NULL,
merge_b, mine_abspath, svn_node_unknown,
scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
{
if (state)
*state = obstr_state;
return SVN_NO_ERROR;
}
}
SVN_ERR(svn_io_check_path(mine_abspath, &kind, scratch_pool));
switch (kind)
{
case svn_node_file:
{
svn_boolean_t same;
SVN_ERR(files_same_p(&same, older_abspath, original_props,
mine_abspath, merge_b->ctx->wc_ctx,
scratch_pool));
if (same || merge_b->force || merge_b->record_only )
{
SVN_ERR(svn_client__wc_delete(mine_abspath, TRUE,
merge_b->dry_run, FALSE, NULL, NULL,
merge_b->ctx, scratch_pool));
if (state)
*state = svn_wc_notify_state_changed;
}
else
{
SVN_ERR(tree_conflict(merge_b, mine_abspath, svn_node_file,
svn_wc_conflict_action_delete,
svn_wc_conflict_reason_edited));
if (tree_conflicted)
*tree_conflicted = TRUE;
if (state)
*state = svn_wc_notify_state_obstructed;
}
}
break;
case svn_node_dir:
SVN_ERR(tree_conflict(merge_b, mine_abspath, svn_node_file,
svn_wc_conflict_action_delete,
svn_wc_conflict_reason_obstructed));
if (tree_conflicted)
*tree_conflicted = TRUE;
if (state)
*state = svn_wc_notify_state_obstructed;
break;
case svn_node_none:
SVN_ERR(tree_conflict(merge_b, mine_abspath, svn_node_file,
svn_wc_conflict_action_delete,
svn_wc_conflict_reason_deleted));
if (tree_conflicted)
*tree_conflicted = TRUE;
if (state)
*state = svn_wc_notify_state_missing;
break;
default:
if (state)
*state = svn_wc_notify_state_unknown;
break;
}
return SVN_NO_ERROR;
}
static svn_error_t *
merge_dir_added(svn_wc_notify_state_t *state,
svn_boolean_t *tree_conflicted,
svn_boolean_t *skip,
svn_boolean_t *skip_children,
const char *local_abspath,
svn_revnum_t rev,
const char *copyfrom_path,
svn_revnum_t copyfrom_revision,
void *baton,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = baton;
svn_node_kind_t kind;
const char *copyfrom_url = NULL, *child;
svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
const char *parent_abspath;
svn_boolean_t is_versioned;
svn_boolean_t is_deleted;
if (merge_b->record_only)
{
if (state)
*state = svn_wc_notify_state_unchanged;
return SVN_NO_ERROR;
}
parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
child = svn_dirent_is_child(merge_b->target_abspath, local_abspath, NULL);
SVN_ERR_ASSERT(child != NULL);
if (merge_b->same_repos)
{
copyfrom_url = svn_path_url_add_component2(merge_b->merge_source.url2,
child, scratch_pool);
copyfrom_rev = rev;
SVN_ERR(check_repos_match(merge_b, parent_abspath, copyfrom_url,
scratch_pool));
}
{
svn_wc_notify_state_t obstr_state;
SVN_ERR(perform_obstruction_check(&obstr_state, NULL,
&is_deleted, &kind,
merge_b, local_abspath, svn_node_unknown,
scratch_pool));
is_versioned = (kind == svn_node_dir) || (kind == svn_node_file);
if (obstr_state == svn_wc_notify_state_obstructed
&& (is_deleted || kind == svn_node_none))
{
svn_node_kind_t disk_kind;
SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, scratch_pool));
if (disk_kind == svn_node_dir)
{
obstr_state = svn_wc_notify_state_inapplicable;
kind = svn_node_dir;
}
}
if (obstr_state != svn_wc_notify_state_inapplicable)
{
if (state && merge_b->dry_run && merge_b->added_path
&& svn_dirent_is_child(merge_b->added_path, local_abspath, NULL))
{
*state = svn_wc_notify_state_changed;
}
else if (state)
*state = obstr_state;
return SVN_NO_ERROR;
}
if (is_deleted)
kind = svn_node_none;
}
switch (kind)
{
case svn_node_none:
if (merge_b->dry_run)
{
merge_b->added_path = apr_pstrdup(merge_b->pool, local_abspath);
apr_hash_set(merge_b->dry_run_added, merge_b->added_path,
APR_HASH_KEY_STRING, merge_b->added_path);
}
else
{
SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT,
scratch_pool));
SVN_ERR(svn_wc_add4(merge_b->ctx->wc_ctx, local_abspath,
svn_depth_infinity,
copyfrom_url, copyfrom_rev,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
NULL, NULL,
scratch_pool));
}
if (state)
*state = svn_wc_notify_state_changed;
break;
case svn_node_dir:
if (! is_versioned || is_deleted)
{
if (!merge_b->dry_run)
SVN_ERR(svn_wc_add4(merge_b->ctx->wc_ctx, local_abspath,
svn_depth_infinity,
copyfrom_url, copyfrom_rev,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
NULL, NULL,
scratch_pool));
else
merge_b->added_path = apr_pstrdup(merge_b->pool, local_abspath);
if (state)
*state = svn_wc_notify_state_changed;
}
else
{
if (dry_run_deleted_p(merge_b, local_abspath))
{
if (state)
*state = svn_wc_notify_state_changed;
}
else
{
SVN_ERR(tree_conflict_on_add(merge_b, local_abspath,
svn_node_dir,
svn_wc_conflict_action_add,
svn_wc_conflict_reason_added));
if (tree_conflicted)
*tree_conflicted = TRUE;
if (state)
*state = svn_wc_notify_state_obstructed;
}
}
break;
case svn_node_file:
if (merge_b->dry_run)
merge_b->added_path = NULL;
if (is_versioned && dry_run_deleted_p(merge_b, local_abspath))
{
if (state)
*state = svn_wc_notify_state_changed;
}
else
{
SVN_ERR(tree_conflict_on_add(merge_b, local_abspath, svn_node_dir,
svn_wc_conflict_action_add,
svn_wc_conflict_reason_obstructed));
if (tree_conflicted)
*tree_conflicted = TRUE;
if (state)
*state = svn_wc_notify_state_obstructed;
}
break;
default:
if (merge_b->dry_run)
merge_b->added_path = NULL;
if (state)
*state = svn_wc_notify_state_unknown;
break;
}
return SVN_NO_ERROR;
}
static svn_error_t *
merge_dir_deleted(svn_wc_notify_state_t *state,
svn_boolean_t *tree_conflicted,
const char *local_abspath,
void *baton,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = baton;
svn_node_kind_t kind;
svn_error_t *err;
svn_boolean_t is_versioned;
svn_boolean_t is_deleted;
if (merge_b->record_only)
{
if (state)
*state = svn_wc_notify_state_unchanged;
return SVN_NO_ERROR;
}
{
svn_wc_notify_state_t obstr_state;
SVN_ERR(perform_obstruction_check(&obstr_state, NULL,
&is_deleted, &kind,
merge_b, local_abspath, svn_node_unknown,
scratch_pool));
is_versioned = (kind == svn_node_dir) || (kind == svn_node_file);
if (obstr_state != svn_wc_notify_state_inapplicable)
{
if (state)
*state = obstr_state;
return SVN_NO_ERROR;
}
if (is_deleted)
kind = svn_node_none;
}
if (merge_b->dry_run)
{
const char *wcpath = apr_pstrdup(merge_b->pool, local_abspath);
apr_hash_set(merge_b->dry_run_deletions, wcpath,
APR_HASH_KEY_STRING, wcpath);
}
switch (kind)
{
case svn_node_dir:
{
if (is_versioned && !is_deleted)
{
err = svn_client__wc_delete(local_abspath, merge_b->force,
merge_b->dry_run, FALSE,
NULL, NULL,
merge_b->ctx, scratch_pool);
if (err)
{
svn_error_clear(err);
SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
svn_wc_conflict_action_delete,
svn_wc_conflict_reason_edited));
if (tree_conflicted)
*tree_conflicted = TRUE;
if (state)
*state = svn_wc_notify_state_conflicted;
}
else
{
if (state)
*state = svn_wc_notify_state_changed;
}
}
else
{
SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
svn_wc_conflict_action_delete,
svn_wc_conflict_reason_deleted));
if (tree_conflicted)
*tree_conflicted = TRUE;
}
}
break;
case svn_node_file:
if (state)
*state = svn_wc_notify_state_obstructed;
break;
case svn_node_none:
SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
svn_wc_conflict_action_delete,
svn_wc_conflict_reason_deleted));
if (tree_conflicted)
*tree_conflicted = TRUE;
if (state)
*state = svn_wc_notify_state_missing;
break;
default:
if (state)
*state = svn_wc_notify_state_unknown;
break;
}
return SVN_NO_ERROR;
}
static svn_error_t *
merge_dir_opened(svn_boolean_t *tree_conflicted,
svn_boolean_t *skip,
svn_boolean_t *skip_children,
const char *local_abspath,
svn_revnum_t rev,
void *baton,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = baton;
svn_depth_t parent_depth;
svn_node_kind_t wc_kind;
svn_wc_notify_state_t obstr_state;
svn_boolean_t is_deleted;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR(perform_obstruction_check(&obstr_state, NULL,
&is_deleted, &wc_kind,
merge_b, local_abspath, svn_node_unknown,
scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
{
if (skip_children)
*skip_children = TRUE;
return SVN_NO_ERROR;
}
if (wc_kind != svn_node_dir || is_deleted)
{
if (wc_kind == svn_node_none)
{
SVN_ERR(svn_wc__node_get_depth(&parent_depth, merge_b->ctx->wc_ctx,
svn_dirent_dirname(local_abspath,
scratch_pool),
scratch_pool));
if (parent_depth != svn_depth_unknown &&
parent_depth < svn_depth_immediates)
{
if (skip_children)
*skip_children = TRUE;
return SVN_NO_ERROR;
}
}
if (wc_kind == svn_node_file)
{
SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
svn_wc_conflict_action_edit,
svn_wc_conflict_reason_replaced));
if (tree_conflicted)
*tree_conflicted = TRUE;
}
else if (is_deleted || wc_kind == svn_node_none)
{
SVN_ERR(tree_conflict(merge_b, local_abspath, svn_node_dir,
svn_wc_conflict_action_edit,
svn_wc_conflict_reason_deleted));
if (tree_conflicted)
*tree_conflicted = TRUE;
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
merge_dir_closed(svn_wc_notify_state_t *contentstate,
svn_wc_notify_state_t *propstate,
svn_boolean_t *tree_conflicted,
const char *path,
svn_boolean_t dir_was_added,
void *baton,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = baton;
if (merge_b->dry_run)
svn_hash__clear(merge_b->dry_run_deletions, scratch_pool);
return SVN_NO_ERROR;
}
static const svn_wc_diff_callbacks4_t
merge_callbacks =
{
merge_file_opened,
merge_file_changed,
merge_file_added,
merge_file_deleted,
merge_dir_deleted,
merge_dir_opened,
merge_dir_added,
merge_dir_props_changed,
merge_dir_closed
};
typedef struct notification_receiver_baton_t
{
svn_wc_notify_func2_t wrapped_func;
void *wrapped_baton;
apr_uint32_t nbr_notifications;
apr_uint32_t nbr_operative_notifications;
apr_hash_t *merged_abspaths;
apr_hash_t *skipped_abspaths;
apr_hash_t *added_abspaths;
apr_hash_t *tree_conflicted_abspaths;
svn_boolean_t is_single_file_merge;
apr_array_header_t *children_with_mergeinfo;
int cur_ancestor_index;
merge_cmd_baton_t *merge_b;
apr_pool_t *pool;
} notification_receiver_baton_t;
static int
find_nearest_ancestor(const apr_array_header_t *children_with_mergeinfo,
svn_boolean_t path_is_own_ancestor,
const char *path)
{
int i;
int ancestor_index = 0;
if (!children_with_mergeinfo)
return 0;
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
if (svn_dirent_is_ancestor(child->abspath, path)
&& (path_is_own_ancestor
|| svn_path_compare_paths(child->abspath, path) != 0))
ancestor_index = i;
}
return ancestor_index;
}
#define IS_OPERATIVE_NOTIFICATION(notify) \
(notify->content_state == svn_wc_notify_state_conflicted \
|| notify->content_state == svn_wc_notify_state_merged \
|| notify->content_state == svn_wc_notify_state_changed \
|| notify->prop_state == svn_wc_notify_state_conflicted \
|| notify->prop_state == svn_wc_notify_state_merged \
|| notify->prop_state == svn_wc_notify_state_changed \
|| notify->action == svn_wc_notify_update_add \
|| notify->action == svn_wc_notify_tree_conflict)
static void
notification_receiver(void *baton, const svn_wc_notify_t *notify,
apr_pool_t *pool)
{
notification_receiver_baton_t *notify_b = baton;
svn_boolean_t is_operative_notification = FALSE;
if (notify_b->merge_b->record_only
&& (notify->action != svn_wc_notify_update_update
&& notify->action != svn_wc_notify_merge_record_info_begin))
return;
if (IS_OPERATIVE_NOTIFICATION(notify))
{
notify_b->nbr_operative_notifications++;
is_operative_notification = TRUE;
}
if (notify_b->merge_b->sources_ancestral
|| notify_b->merge_b->reintegrate_merge)
{
if (notify->content_state == svn_wc_notify_state_merged
|| notify->content_state == svn_wc_notify_state_changed
|| notify->prop_state == svn_wc_notify_state_merged
|| notify->prop_state == svn_wc_notify_state_changed
|| notify->action == svn_wc_notify_update_add)
{
const char *merged_path = apr_pstrdup(notify_b->pool, notify->path);
if (notify_b->merged_abspaths == NULL)
notify_b->merged_abspaths = apr_hash_make(notify_b->pool);
apr_hash_set(notify_b->merged_abspaths, merged_path,
APR_HASH_KEY_STRING, merged_path);
}
if (notify->action == svn_wc_notify_skip)
{
const char *skipped_path = apr_pstrdup(notify_b->pool, notify->path);
if (notify_b->skipped_abspaths == NULL)
notify_b->skipped_abspaths = apr_hash_make(notify_b->pool);
apr_hash_set(notify_b->skipped_abspaths, skipped_path,
APR_HASH_KEY_STRING, skipped_path);
}
if (notify->action == svn_wc_notify_tree_conflict)
{
const char *tree_conflicted_path = apr_pstrdup(notify_b->pool,
notify->path);
if (notify_b->tree_conflicted_abspaths == NULL)
notify_b->tree_conflicted_abspaths =
apr_hash_make(notify_b->pool);
apr_hash_set(notify_b->tree_conflicted_abspaths,
tree_conflicted_path, APR_HASH_KEY_STRING,
tree_conflicted_path);
}
if (notify->action == svn_wc_notify_update_add)
{
svn_boolean_t root_of_added_subtree = TRUE;
if (notify_b->added_abspaths == NULL)
{
notify_b->added_abspaths = apr_hash_make(notify_b->pool);
}
else
{
const char *added_path_parent =
svn_dirent_dirname(notify->path, pool);
apr_pool_t *subpool = svn_pool_create(pool);
while (strcmp(notify_b->merge_b->target_abspath,
added_path_parent))
{
if (apr_hash_get(notify_b->added_abspaths,
added_path_parent,
APR_HASH_KEY_STRING))
{
root_of_added_subtree = FALSE;
break;
}
added_path_parent = svn_dirent_dirname(
added_path_parent, subpool);
}
svn_pool_destroy(subpool);
}
if (root_of_added_subtree)
{
const char *added_root_path = apr_pstrdup(notify_b->pool,
notify->path);
apr_hash_set(notify_b->added_abspaths, added_root_path,
APR_HASH_KEY_STRING, added_root_path);
}
}
if (notify->action == svn_wc_notify_update_delete
&& notify_b->added_abspaths)
{
apr_hash_set(notify_b->added_abspaths, notify->path,
APR_HASH_KEY_STRING, NULL);
}
}
if (notify_b->merge_b->sources_ancestral)
{
notify_b->nbr_notifications++;
if (!(notify_b->is_single_file_merge) && is_operative_notification)
{
int new_nearest_ancestor_index =
find_nearest_ancestor(
notify_b->children_with_mergeinfo,
notify->action != svn_wc_notify_update_delete,
notify->path);
if (new_nearest_ancestor_index != notify_b->cur_ancestor_index)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(notify_b->children_with_mergeinfo,
new_nearest_ancestor_index,
svn_client__merge_path_t *);
notify_b->cur_ancestor_index = new_nearest_ancestor_index;
if (!child->absent && child->remaining_ranges->nelts > 0
&& !(new_nearest_ancestor_index == 0
&& child->remaining_ranges == 0))
{
svn_wc_notify_t *notify_merge_begin;
notify_merge_begin =
svn_wc_create_notify(child->abspath,
notify_b->merge_b->same_repos
? svn_wc_notify_merge_begin
: svn_wc_notify_foreign_merge_begin,
pool);
notify_merge_begin->merge_range =
APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *);
if (notify_b->wrapped_func)
(*notify_b->wrapped_func)(notify_b->wrapped_baton,
notify_merge_begin, pool);
}
}
}
}
else if (!(notify_b->is_single_file_merge)
&& notify_b->nbr_operative_notifications == 1
&& is_operative_notification)
{
svn_wc_notify_t *notify_merge_begin;
notify_merge_begin =
svn_wc_create_notify(notify_b->merge_b->target_abspath,
notify_b->merge_b->same_repos
? svn_wc_notify_merge_begin
: svn_wc_notify_foreign_merge_begin,
pool);
if (notify_b->wrapped_func)
(*notify_b->wrapped_func)(notify_b->wrapped_baton, notify_merge_begin,
pool);
}
if (notify_b->wrapped_func)
(*notify_b->wrapped_func)(notify_b->wrapped_baton, notify, pool);
}
static svn_error_t *
rangelist_intersect_range(apr_array_header_t **out_rangelist,
const apr_array_header_t *in_rangelist,
svn_revnum_t rev1,
svn_revnum_t rev2,
svn_boolean_t consider_inheritance,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(rev1 <= rev2);
if (rev1 < rev2)
{
apr_array_header_t *simple_rangelist =
svn_rangelist__initialize(rev1, rev2, TRUE, scratch_pool);
SVN_ERR(svn_rangelist_intersect(out_rangelist,
simple_rangelist, in_rangelist,
consider_inheritance, result_pool));
}
else
{
*out_rangelist = apr_array_make(result_pool, 0,
sizeof(svn_merge_range_t *));
}
return SVN_NO_ERROR;
}
static svn_error_t *
adjust_deleted_subtree_ranges(svn_client__merge_path_t *child,
svn_client__merge_path_t *parent,
const char *mergeinfo_path,
svn_revnum_t revision1,
svn_revnum_t revision2,
const char *primary_url,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t is_rollback = revision2 < revision1;
svn_revnum_t younger_rev = is_rollback ? revision1 : revision2;
svn_revnum_t peg_rev = younger_rev;
svn_revnum_t older_rev = is_rollback ? revision2 : revision1;
apr_array_header_t *segments;
const char *rel_source_path;
const char *session_url;
svn_error_t *err;
SVN_ERR_ASSERT(parent->remaining_ranges);
SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
SVN_ERR(svn_client__path_relative_to_root(&rel_source_path,
ctx->wc_ctx,
primary_url,
session_url,
FALSE,
ra_session,
scratch_pool,
scratch_pool));
err = svn_client__repos_location_segments(&segments, ra_session,
rel_source_path, peg_rev,
younger_rev, older_rev, ctx,
scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_FS_NOT_FOUND
|| err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
{
svn_node_kind_t kind;
svn_error_clear(err);
err = NULL;
SVN_ERR(svn_ra_check_path(ra_session, rel_source_path,
older_rev, &kind, scratch_pool));
if (kind == svn_node_none)
{
child->remaining_ranges =
svn_rangelist_dup(parent->remaining_ranges, scratch_pool);
}
else
{
apr_array_header_t *deleted_rangelist;
svn_revnum_t rev_primary_url_deleted;
SVN_ERR(svn_ra_get_deleted_rev(ra_session, rel_source_path,
older_rev, younger_rev,
&rev_primary_url_deleted,
scratch_pool));
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev_primary_url_deleted));
if (is_rollback)
{
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
scratch_pool));
SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
scratch_pool));
}
SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
child->remaining_ranges,
older_rev,
rev_primary_url_deleted - 1,
FALSE,
scratch_pool, scratch_pool));
SVN_ERR(rangelist_intersect_range(&deleted_rangelist,
parent->remaining_ranges,
rev_primary_url_deleted - 1,
peg_rev,
FALSE,
scratch_pool, scratch_pool));
SVN_ERR(svn_rangelist_merge(&(child->remaining_ranges),
deleted_rangelist, scratch_pool));
if (is_rollback)
{
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
scratch_pool));
SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
scratch_pool));
}
}
}
else
{
return svn_error_trace(err);
}
}
else
{
apr_array_header_t *non_existent_rangelist;
svn_location_segment_t *segment =
APR_ARRAY_IDX(segments, (segments->nelts - 1),
svn_location_segment_t *);
if (segment->range_start == older_rev)
{
return SVN_NO_ERROR;
}
if (is_rollback)
{
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
scratch_pool));
SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
scratch_pool));
}
SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
child->remaining_ranges,
segment->range_start, peg_rev,
FALSE, scratch_pool, scratch_pool));
SVN_ERR(rangelist_intersect_range(&non_existent_rangelist,
parent->remaining_ranges,
older_rev, segment->range_start,
FALSE, scratch_pool, scratch_pool));
SVN_ERR(svn_rangelist_merge(&(child->remaining_ranges),
non_existent_rangelist, scratch_pool));
if (is_rollback)
{
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
scratch_pool));
SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
scratch_pool));
}
}
child->remaining_ranges = svn_rangelist_dup(child->remaining_ranges,
result_pool);
return SVN_NO_ERROR;
}
static svn_error_t *
fix_deleted_subtree_ranges(const char *url1,
svn_revnum_t revision1,
const char *url2,
svn_revnum_t revision2,
svn_ra_session_t *ra_session,
notification_receiver_baton_t *notify_b,
merge_cmd_baton_t *merge_b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
const char *source_root_url;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
svn_boolean_t is_rollback = revision2 < revision1;
SVN_ERR(svn_ra_get_repos_root2(ra_session, &source_root_url, scratch_pool));
for (i = 1; i < notify_b->children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(notify_b->children_with_mergeinfo, i,
svn_client__merge_path_t *);
svn_client__merge_path_t *parent;
apr_array_header_t *deleted_rangelist, *added_rangelist;
int parent_index;
SVN_ERR_ASSERT(child);
if (child->absent)
continue;
svn_pool_clear(iterpool);
parent_index = find_nearest_ancestor(notify_b->children_with_mergeinfo,
FALSE, child->abspath);
parent = APR_ARRAY_IDX(notify_b->children_with_mergeinfo, parent_index,
svn_client__merge_path_t *);
SVN_ERR_ASSERT(parent);
if (is_rollback)
{
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
}
SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
child->remaining_ranges,
parent->remaining_ranges,
TRUE, iterpool));
if (is_rollback)
{
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
}
if (deleted_rangelist->nelts || added_rangelist->nelts)
{
const char *child_mergeinfo_path;
const char *child_primary_source_url;
const char *child_repos_src_path =
svn_dirent_is_child(merge_b->target_abspath, child->abspath,
iterpool);
SVN_ERR_ASSERT(child_repos_src_path);
child_primary_source_url =
svn_path_url_add_component2((revision1 < revision2) ? url2 : url1,
child_repos_src_path, iterpool);
SVN_ERR(svn_client__path_relative_to_root(&child_mergeinfo_path,
merge_b->ctx->wc_ctx,
child_primary_source_url,
source_root_url,
TRUE, ra_session,
iterpool, iterpool));
SVN_ERR(adjust_deleted_subtree_ranges(child, parent,
child_mergeinfo_path,
revision1, revision2,
child_primary_source_url,
ra_session,
merge_b->ctx, result_pool,
iterpool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
get_full_mergeinfo(svn_mergeinfo_t *recorded_mergeinfo,
svn_mergeinfo_t *implicit_mergeinfo,
svn_boolean_t *inherited,
svn_mergeinfo_inheritance_t inherit,
svn_ra_session_t *ra_session,
const char *target_abspath,
svn_revnum_t start,
svn_revnum_t end,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t inherited_mergeinfo = FALSE;
if (recorded_mergeinfo)
{
svn_boolean_t inherited_from_repos;
SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(recorded_mergeinfo,
&inherited_mergeinfo,
&inherited_from_repos,
FALSE,
inherit, ra_session,
target_abspath,
ctx, result_pool));
if (inherited)
*inherited = inherited_mergeinfo;
}
if (implicit_mergeinfo)
{
const char *repos_root;
const char *repos_relpath;
const char *session_url = NULL;
svn_revnum_t target_rev;
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start)
&& SVN_IS_VALID_REVNUM(end)
&& (start > end));
SVN_ERR(svn_wc__node_get_origin(FALSE, &target_rev, &repos_relpath,
&repos_root, NULL, NULL,
ctx->wc_ctx, target_abspath, FALSE,
scratch_pool, scratch_pool));
if (! repos_relpath)
{
*implicit_mergeinfo = apr_hash_make(result_pool);
}
else if (target_rev <= end)
{
*implicit_mergeinfo = apr_hash_make(result_pool);
}
else
{
svn_opt_revision_t peg_revision;
const char *url;
url = svn_path_url_add_component2(repos_root, repos_relpath,
scratch_pool);
SVN_ERR(svn_client__ensure_ra_session_url(&session_url,
ra_session, url,
scratch_pool));
if (target_rev < start)
start = target_rev;
peg_revision.kind = svn_opt_revision_number;
peg_revision.value.number = target_rev;
SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo,
NULL,
url, &peg_revision,
start, end,
ra_session, ctx,
result_pool));
SVN_ERR(svn_client__ensure_ra_session_url(&session_url,
ra_session, session_url,
scratch_pool));
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
inherit_implicit_mergeinfo_from_parent(svn_client__merge_path_t *parent,
svn_client__merge_path_t *child,
svn_revnum_t revision1,
svn_revnum_t revision2,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *path_diff;
SVN_ERR_ASSERT(parent);
SVN_ERR_ASSERT(child);
if (!parent->implicit_mergeinfo)
SVN_ERR(get_full_mergeinfo(NULL, &(parent->implicit_mergeinfo),
NULL, svn_mergeinfo_inherited,
ra_session, child->abspath,
MAX(revision1, revision2),
MIN(revision1, revision2),
ctx, result_pool, scratch_pool));
child->implicit_mergeinfo = apr_hash_make(result_pool);
path_diff = svn_dirent_is_child(parent->abspath, child->abspath,
scratch_pool);
SVN_ERR_ASSERT(path_diff);
SVN_ERR(svn_client__adjust_mergeinfo_source_paths(
child->implicit_mergeinfo, path_diff,
parent->implicit_mergeinfo, result_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
ensure_implicit_mergeinfo(svn_client__merge_path_t *parent,
svn_client__merge_path_t *child,
svn_boolean_t child_inherits_parent,
svn_revnum_t revision1,
svn_revnum_t revision2,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
if (child->implicit_mergeinfo)
return SVN_NO_ERROR;
if (child_inherits_parent)
SVN_ERR(inherit_implicit_mergeinfo_from_parent(parent,
child,
revision1,
revision2,
ra_session,
ctx,
result_pool,
scratch_pool));
else
SVN_ERR(get_full_mergeinfo(NULL,
&(child->implicit_mergeinfo),
NULL, svn_mergeinfo_inherited,
ra_session, child->abspath,
MAX(revision1, revision2),
MIN(revision1, revision2),
ctx, result_pool, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
filter_merged_revisions(svn_client__merge_path_t *parent,
svn_client__merge_path_t *child,
const char *mergeinfo_path,
svn_mergeinfo_t target_mergeinfo,
svn_revnum_t revision1,
svn_revnum_t revision2,
svn_boolean_t child_inherits_implicit,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_array_header_t *requested_rangelist, *target_rangelist,
*target_implicit_rangelist, *explicit_rangelist;
requested_rangelist = svn_rangelist__initialize(revision1, revision2,
TRUE, scratch_pool);
if (revision1 > revision2)
{
apr_array_header_t *added_rangelist, *deleted_rangelist;
SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
if (target_mergeinfo)
target_rangelist = apr_hash_get(target_mergeinfo,
mergeinfo_path, APR_HASH_KEY_STRING);
else
target_rangelist = NULL;
if (target_rangelist)
{
SVN_ERR(svn_rangelist_intersect(&explicit_rangelist,
target_rangelist,
requested_rangelist,
FALSE, scratch_pool));
}
else
{
explicit_rangelist =
apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *));
}
SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
requested_rangelist, explicit_rangelist,
FALSE, scratch_pool));
if (deleted_rangelist->nelts == 0)
{
SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
child->remaining_ranges = svn_rangelist_dup(requested_rangelist,
result_pool);
}
else
{
apr_array_header_t *implicit_rangelist;
SVN_ERR(ensure_implicit_mergeinfo(parent,
child,
child_inherits_implicit,
revision1,
revision2,
ra_session,
ctx,
result_pool,
scratch_pool));
target_implicit_rangelist = apr_hash_get(child->implicit_mergeinfo,
mergeinfo_path,
APR_HASH_KEY_STRING);
if (target_implicit_rangelist)
SVN_ERR(svn_rangelist_intersect(&implicit_rangelist,
target_implicit_rangelist,
requested_rangelist,
FALSE, scratch_pool));
else
implicit_rangelist = apr_array_make(scratch_pool, 0,
sizeof(svn_merge_range_t *));
SVN_ERR(svn_rangelist_merge(&implicit_rangelist,
explicit_rangelist, scratch_pool));
SVN_ERR(svn_rangelist_reverse(implicit_rangelist, scratch_pool));
child->remaining_ranges = svn_rangelist_dup(implicit_rangelist,
result_pool);
}
}
else
{
if (target_mergeinfo)
target_rangelist = apr_hash_get(target_mergeinfo, mergeinfo_path,
APR_HASH_KEY_STRING);
else
target_rangelist = NULL;
if (target_rangelist)
{
SVN_ERR(svn_rangelist_remove(&explicit_rangelist,
target_rangelist,
requested_rangelist, FALSE,
scratch_pool));
}
else
{
explicit_rangelist = svn_rangelist_dup(requested_rangelist,
scratch_pool);
}
if (explicit_rangelist->nelts == 0)
{
child->remaining_ranges =
apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *));
}
else
#ifdef SVN_MERGE__ALLOW_ALL_FORWARD_MERGES_FROM_SELF
{
child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
pool);
}
#else
{
SVN_ERR(ensure_implicit_mergeinfo(parent,
child,
child_inherits_implicit,
revision1,
revision2,
ra_session,
ctx,
result_pool,
scratch_pool));
target_implicit_rangelist = apr_hash_get(child->implicit_mergeinfo,
mergeinfo_path,
APR_HASH_KEY_STRING);
if (target_implicit_rangelist)
SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
target_implicit_rangelist,
explicit_rangelist,
FALSE, result_pool));
else
child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
result_pool);
}
#endif
}
return SVN_NO_ERROR;
}
static svn_error_t *
calculate_remaining_ranges(svn_client__merge_path_t *parent,
svn_client__merge_path_t *child,
const char *source_root_url,
const char *url1,
svn_revnum_t revision1,
const char *url2,
svn_revnum_t revision2,
svn_mergeinfo_t target_mergeinfo,
const apr_array_header_t *implicit_src_gap,
svn_boolean_t child_inherits_implicit,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *mergeinfo_path;
const char *primary_url = (revision1 < revision2) ? url2 : url1;
svn_mergeinfo_t adjusted_target_mergeinfo = NULL;
svn_revnum_t child_base_revision;
SVN_ERR(svn_client__path_relative_to_root(&mergeinfo_path, ctx->wc_ctx,
primary_url, source_root_url, TRUE,
ra_session, result_pool,
scratch_pool));
if (implicit_src_gap && child->pre_merge_mergeinfo)
{
apr_array_header_t *explicit_mergeinfo_gap_ranges =
apr_hash_get(child->pre_merge_mergeinfo, mergeinfo_path,
APR_HASH_KEY_STRING);
if (explicit_mergeinfo_gap_ranges)
{
svn_mergeinfo_t gap_mergeinfo = apr_hash_make(scratch_pool);
apr_hash_set(gap_mergeinfo, mergeinfo_path, APR_HASH_KEY_STRING,
implicit_src_gap);
SVN_ERR(svn_mergeinfo_remove2(&adjusted_target_mergeinfo,
gap_mergeinfo, target_mergeinfo,
FALSE, result_pool, scratch_pool));
}
}
else
{
adjusted_target_mergeinfo = target_mergeinfo;
}
SVN_ERR(filter_merged_revisions(parent, child, mergeinfo_path,
adjusted_target_mergeinfo,
revision1, revision2,
child_inherits_implicit,
ra_session, ctx, result_pool,
scratch_pool));
SVN_ERR(svn_wc__node_get_base_rev(&child_base_revision, ctx->wc_ctx,
child->abspath, scratch_pool));
if (SVN_IS_VALID_REVNUM(child_base_revision)
&& ((child->remaining_ranges)->nelts == 0)
&& (revision2 < revision1)
&& (child_base_revision <= revision2))
{
svn_error_t *err;
const char *start_url;
svn_opt_revision_t requested, unspec, pegrev, *start_revision;
unspec.kind = svn_opt_revision_unspecified;
requested.kind = svn_opt_revision_number;
requested.value.number = child_base_revision;
pegrev.kind = svn_opt_revision_number;
pegrev.value.number = revision1;
err = svn_client__repos_locations(&start_url, &start_revision,
NULL, NULL, ra_session, url1,
&pegrev, &requested,
&unspec, ctx, scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_FS_NOT_FOUND
|| err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
svn_error_clear(err);
else
return svn_error_trace(err);
}
else
{
const char *url;
SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, child->abspath,
scratch_pool, scratch_pool));
if (strcmp(start_url, url) == 0)
return svn_error_create(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
_("Cannot reverse-merge a range from a "
"path's own future history; try "
"updating first"));
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
find_gaps_in_merge_source_history(svn_revnum_t *gap_start,
svn_revnum_t *gap_end,
const char *merge_src_canon_path,
const char *url1,
svn_revnum_t revision1,
const char *url2,
svn_revnum_t revision2,
svn_ra_session_t *ra_session,
merge_cmd_baton_t *merge_b,
apr_pool_t *scratch_pool)
{
svn_mergeinfo_t implicit_src_mergeinfo;
svn_opt_revision_t peg_rev;
svn_revnum_t young_rev = MAX(revision1, revision2);
svn_revnum_t old_rev = MIN(revision1, revision2);
apr_array_header_t *rangelist;
const char *url = (revision2 < revision1) ? url1 : url2;
*gap_start = *gap_end = SVN_INVALID_REVNUM;
peg_rev.kind = svn_opt_revision_number;
peg_rev.value.number = young_rev;
SVN_ERR(svn_client__get_history_as_mergeinfo(&implicit_src_mergeinfo, NULL,
url, &peg_rev, young_rev,
old_rev, ra_session,
merge_b->ctx, scratch_pool));
rangelist = apr_hash_get(implicit_src_mergeinfo,
merge_src_canon_path,
APR_HASH_KEY_STRING);
if (!rangelist)
return SVN_NO_ERROR;
if (rangelist->nelts > 1)
{
SVN_ERR_ASSERT(apr_hash_count(implicit_src_mergeinfo) == 1);
*gap_start = MIN(revision1, revision2);
*gap_end = (APR_ARRAY_IDX(rangelist,
rangelist->nelts - 1,
svn_merge_range_t *))->start;
}
else if (apr_hash_count(implicit_src_mergeinfo) > 1)
{
apr_array_header_t *requested_rangelist =
svn_rangelist__initialize(MIN(revision1, revision2),
MAX(revision1, revision2),
TRUE, scratch_pool);
apr_array_header_t *implicit_rangelist =
apr_array_make(scratch_pool, 2, sizeof(svn_merge_range_t *));
apr_array_header_t *gap_rangelist;
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, implicit_src_mergeinfo);
hi;
hi = apr_hash_next(hi))
{
apr_array_header_t *value = svn__apr_hash_index_val(hi);
SVN_ERR(svn_rangelist_merge(&implicit_rangelist, value,
scratch_pool));
}
SVN_ERR(svn_rangelist_remove(&gap_rangelist, implicit_rangelist,
requested_rangelist, FALSE,
scratch_pool));
if (gap_rangelist->nelts)
{
svn_merge_range_t *gap_range =
APR_ARRAY_IDX(gap_rangelist, 0, svn_merge_range_t *);
*gap_start = gap_range->start;
*gap_end = gap_range->end;
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
populate_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
const char *source_root_url,
const char *url1,
svn_revnum_t revision1,
const char *url2,
svn_revnum_t revision2,
svn_boolean_t honor_mergeinfo,
svn_ra_session_t *ra_session,
const char *parent_merge_src_canon_path,
merge_cmd_baton_t *merge_b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
svn_revnum_t gap_start, gap_end;
svn_boolean_t child_inherits_implicit;
svn_client__merge_path_t *parent;
int parent_index;
if (! honor_mergeinfo || merge_b->record_only)
{
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_client__merge_path_t *);
parent = NULL;
svn_pool_clear(iterpool);
if (i == 0)
{
SVN_ERR(get_full_mergeinfo(NULL,
&(child->implicit_mergeinfo),
NULL,
svn_mergeinfo_inherited, ra_session,
child->abspath,
MAX(revision1, revision2),
MIN(revision1, revision2),
merge_b->ctx, result_pool,
iterpool));
}
else
{
parent_index = find_nearest_ancestor(children_with_mergeinfo,
FALSE, child->abspath);
parent = APR_ARRAY_IDX(children_with_mergeinfo, parent_index,
svn_client__merge_path_t *);
SVN_ERR_ASSERT(parent);
child_inherits_implicit = (parent && !child->switched);
SVN_ERR(ensure_implicit_mergeinfo(parent, child,
child_inherits_implicit,
revision1, revision2,
ra_session, merge_b->ctx,
result_pool, iterpool));
}
child->remaining_ranges = svn_rangelist__initialize(revision1,
revision2,
TRUE,
result_pool);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
SVN_ERR(find_gaps_in_merge_source_history(&gap_start, &gap_end,
parent_merge_src_canon_path,
url1, revision1,
url2, revision2,
ra_session, merge_b,
iterpool));
if (SVN_IS_VALID_REVNUM(gap_start) && SVN_IS_VALID_REVNUM(gap_end))
merge_b->implicit_src_gap = svn_rangelist__initialize(gap_start, gap_end,
TRUE, result_pool);
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
const char *child_repos_path;
const char *child_url1, *child_url2;
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
parent = NULL;
SVN_ERR_ASSERT(child);
if (child->absent)
continue;
svn_pool_clear(iterpool);
child_repos_path = svn_dirent_is_child(merge_b->target_abspath,
child->abspath, iterpool);
if (!child_repos_path)
child_repos_path = "";
child_url1 = svn_path_url_add_component2(url1, child_repos_path,
iterpool);
child_url2 = svn_path_url_add_component2(url2, child_repos_path,
iterpool);
SVN_ERR(get_full_mergeinfo(
child->pre_merge_mergeinfo ? NULL : &(child->pre_merge_mergeinfo),
(i == 0) ? &(child->implicit_mergeinfo) : NULL,
&(child->inherited_mergeinfo),
svn_mergeinfo_inherited, ra_session,
child->abspath,
MAX(revision1, revision2),
MIN(revision1, revision2),
merge_b->ctx, result_pool, iterpool));
if (i > 0)
{
parent_index = find_nearest_ancestor(children_with_mergeinfo,
FALSE, child->abspath);
parent = APR_ARRAY_IDX(children_with_mergeinfo, parent_index,
svn_client__merge_path_t *);
SVN_ERR_ASSERT(parent);
}
child_inherits_implicit = (parent && !child->switched);
SVN_ERR(calculate_remaining_ranges(parent, child,
source_root_url,
child_url1, revision1,
child_url2, revision2,
child->pre_merge_mergeinfo,
merge_b->implicit_src_gap,
child_inherits_implicit,
ra_session,
merge_b->ctx, result_pool,
iterpool));
if (child->remaining_ranges->nelts
&& merge_b->implicit_src_gap)
{
int j;
svn_revnum_t start, end;
svn_boolean_t proper_subset = FALSE;
svn_boolean_t overlaps_or_adjoins = FALSE;
if (revision1 > revision2)
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
for (j = 0; j < child->remaining_ranges->nelts; j++)
{
start = (APR_ARRAY_IDX(child->remaining_ranges, j,
svn_merge_range_t *))->start;
end = (APR_ARRAY_IDX(child->remaining_ranges, j,
svn_merge_range_t *))->end;
if ((start <= gap_start && gap_end < end)
|| (start < gap_start && gap_end <= end))
{
proper_subset = TRUE;
break;
}
else if ((gap_start == start) && (end == gap_end))
{
break;
}
else if (gap_start <= end && start <= gap_end)
{
overlaps_or_adjoins = TRUE;
break;
}
}
if (!proper_subset)
{
if (overlaps_or_adjoins)
SVN_ERR(svn_rangelist_merge(&(child->remaining_ranges),
merge_b->implicit_src_gap,
result_pool));
else
SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
merge_b->implicit_src_gap,
child->remaining_ranges, FALSE,
result_pool));
}
if (revision1 > revision2)
SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
calculate_merge_inheritance(apr_array_header_t *rangelist,
svn_boolean_t *rangelist_inheritance,
const char *local_abspath,
svn_boolean_t wc_path_is_merge_target,
svn_boolean_t wc_path_has_missing_child,
svn_depth_t depth,
svn_wc_context_t *wc_ctx,
apr_pool_t * scratch_pool)
{
svn_node_kind_t path_kind;
SVN_ERR(svn_wc_read_kind(&path_kind, wc_ctx, local_abspath, FALSE,
scratch_pool));
*rangelist_inheritance = TRUE;
if (path_kind == svn_node_file)
{
svn_rangelist__set_inheritance(rangelist, TRUE);
}
else if (path_kind == svn_node_dir)
{
if (wc_path_is_merge_target)
{
if (wc_path_has_missing_child
|| depth == svn_depth_files
|| depth == svn_depth_empty)
{
svn_rangelist__set_inheritance(rangelist, FALSE);
*rangelist_inheritance = FALSE;
}
else
{
svn_rangelist__set_inheritance(rangelist, TRUE);
}
}
else
{
if (wc_path_has_missing_child
|| depth == svn_depth_immediates)
{
svn_rangelist__set_inheritance(rangelist, FALSE);
*rangelist_inheritance = FALSE;
}
else
{
svn_rangelist__set_inheritance(rangelist, TRUE);
}
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
update_wc_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
const char *target_abspath,
const char *repos_rel_path,
apr_hash_t *merges,
svn_boolean_t is_rollback,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
const char *rel_path;
svn_mergeinfo_t mergeinfo;
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, merges); hi; hi = apr_hash_next(hi))
{
const char *local_abspath = svn__apr_hash_index_key(hi);
apr_array_header_t *ranges = svn__apr_hash_index_val(hi);
apr_array_header_t *rangelist;
svn_error_t *err;
const char *local_abspath_rel_to_target;
svn_pool_clear(iterpool);
err = svn_client__parse_mergeinfo(&mergeinfo, ctx->wc_ctx,
local_abspath, iterpool, iterpool);
if (err)
{
if (err->apr_err == SVN_ERR_WC_NOT_LOCKED
|| err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
{
svn_error_clear(err);
continue;
}
else
{
return svn_error_trace(err);
}
}
if (mergeinfo == NULL && ranges->nelts == 0)
{
svn_boolean_t inherited;
SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, &inherited,
svn_mergeinfo_nearest_ancestor,
local_abspath, NULL, NULL,
FALSE, ctx, iterpool, iterpool));
}
if (mergeinfo == NULL)
mergeinfo = apr_hash_make(iterpool);
local_abspath_rel_to_target = svn_dirent_is_child(target_abspath,
local_abspath,
iterpool);
if (local_abspath_rel_to_target)
rel_path = svn_dirent_join(repos_rel_path,
local_abspath_rel_to_target,
iterpool);
else
rel_path = repos_rel_path;
rangelist = apr_hash_get(mergeinfo, rel_path, APR_HASH_KEY_STRING);
if (rangelist == NULL)
rangelist = apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
if (is_rollback)
{
ranges = svn_rangelist_dup(ranges, iterpool);
SVN_ERR(svn_rangelist_reverse(ranges, iterpool));
SVN_ERR(svn_rangelist_remove(&rangelist, ranges, rangelist,
FALSE,
iterpool));
}
else
{
SVN_ERR(svn_rangelist_merge(&rangelist, ranges,
iterpool));
}
apr_hash_set(mergeinfo, rel_path, APR_HASH_KEY_STRING, rangelist);
if (is_rollback && apr_hash_count(mergeinfo) == 0)
mergeinfo = NULL;
svn_mergeinfo__remove_empty_rangelists(mergeinfo, scratch_pool);
if (result_catalog)
{
svn_mergeinfo_t existing_mergeinfo =
apr_hash_get(result_catalog, local_abspath, APR_HASH_KEY_STRING);
apr_pool_t *result_catalog_pool = apr_hash_pool_get(result_catalog);
if (existing_mergeinfo)
SVN_ERR(svn_mergeinfo_merge(mergeinfo, existing_mergeinfo,
result_catalog_pool));
apr_hash_set(result_catalog,
apr_pstrdup(result_catalog_pool, local_abspath),
APR_HASH_KEY_STRING,
svn_mergeinfo_dup(mergeinfo, result_catalog_pool));
}
else
{
err = svn_client__record_wc_mergeinfo(local_abspath, mergeinfo,
TRUE, ctx, iterpool);
if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
{
svn_error_clear(err);
}
else
SVN_ERR(err);
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
record_skips(const char *mergeinfo_path,
const apr_array_header_t *rangelist,
svn_boolean_t is_rollback,
notification_receiver_baton_t *notify_b,
merge_cmd_baton_t *merge_b,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
apr_hash_t *merges;
apr_size_t nbr_skips = (notify_b->skipped_abspaths != NULL ?
apr_hash_count(notify_b->skipped_abspaths) : 0);
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
if (nbr_skips == 0)
return SVN_NO_ERROR;
merges = apr_hash_make(scratch_pool);
for (hi = apr_hash_first(scratch_pool, notify_b->skipped_abspaths); hi;
hi = apr_hash_next(hi))
{
const char *skipped_abspath = svn__apr_hash_index_key(hi);
svn_wc_notify_state_t obstruction_state;
svn_pool_clear(iterpool);
SVN_ERR(perform_obstruction_check(&obstruction_state,
NULL, NULL, NULL,
merge_b, skipped_abspath,
svn_node_unknown, iterpool));
if (obstruction_state == svn_wc_notify_state_obstructed
|| obstruction_state == svn_wc_notify_state_missing)
continue;
apr_hash_set(merges, skipped_abspath,
APR_HASH_KEY_STRING,
apr_array_make(scratch_pool, 0,
sizeof(svn_merge_range_t *)));
if (nbr_skips < notify_b->nbr_notifications)
;
}
SVN_ERR(update_wc_mergeinfo(NULL, merge_b->target_abspath,
mergeinfo_path, merges,
is_rollback, merge_b->ctx, iterpool));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static APR_INLINE svn_error_t *
make_merge_conflict_error(const char *target_wcpath,
svn_merge_range_t *r,
apr_pool_t *scratch_pool)
{
return svn_error_createf
(SVN_ERR_WC_FOUND_CONFLICT, NULL,
_("One or more conflicts were produced while merging r%ld:%ld into\n"
"'%s' --\n"
"resolve all conflicts and rerun the merge to apply the remaining\n"
"unmerged revisions"),
r->start, r->end, svn_dirent_local_style(target_wcpath, scratch_pool));
}
static void
remove_absent_children(const char *target_wcpath,
apr_array_header_t *children_with_mergeinfo,
notification_receiver_baton_t *notify_b)
{
int i;
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo,
i, svn_client__merge_path_t *);
if ((child->absent || child->scheduled_for_deletion)
&& svn_dirent_is_ancestor(target_wcpath, child->abspath))
{
svn_sort__array_delete(children_with_mergeinfo, i--, 1);
}
}
}
static void
remove_children_with_deleted_mergeinfo(merge_cmd_baton_t *merge_b,
notification_receiver_baton_t *notify_b)
{
int i;
if (merge_b->dry_run || !merge_b->paths_with_deleted_mergeinfo)
return;
for (i = 1; i < notify_b->children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(notify_b->children_with_mergeinfo,
i, svn_client__merge_path_t *);
if (apr_hash_get(merge_b->paths_with_deleted_mergeinfo,
child->abspath,
APR_HASH_KEY_STRING))
{
svn_sort__array_delete(notify_b->children_with_mergeinfo, i--, 1);
}
}
}
static svn_error_t *
drive_merge_report_editor(const char *target_abspath,
const char *url1,
svn_revnum_t revision1,
const char *url2,
svn_revnum_t revision2,
const apr_array_header_t *children_with_mergeinfo,
svn_depth_t depth,
notification_receiver_baton_t *notify_b,
merge_cmd_baton_t *merge_b,
apr_pool_t *scratch_pool)
{
const svn_ra_reporter3_t *reporter;
const svn_delta_editor_t *diff_editor;
void *diff_edit_baton;
void *report_baton;
svn_revnum_t target_start;
svn_boolean_t honor_mergeinfo;
const char *old_sess2_url;
svn_boolean_t is_rollback = revision1 > revision2;
honor_mergeinfo = HONOR_MERGEINFO(merge_b);
target_start = revision1;
if (honor_mergeinfo)
{
svn_client__merge_path_t *child;
SVN_ERR_ASSERT(children_with_mergeinfo);
SVN_ERR_ASSERT(children_with_mergeinfo->nelts);
child = APR_ARRAY_IDX(children_with_mergeinfo, 0,
svn_client__merge_path_t *);
SVN_ERR_ASSERT(child);
if (child->remaining_ranges->nelts == 0)
{
target_start = revision2;
}
else
{
svn_merge_range_t *range =
APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *);
if ((!is_rollback && range->start > revision2)
|| (is_rollback && range->start < revision2))
{
target_start = revision2;
}
else
{
target_start = range->start;
}
}
}
SVN_ERR(svn_client__ensure_ra_session_url(&old_sess2_url,
merge_b->ra_session2,
url1, scratch_pool));
SVN_ERR(svn_client__get_diff_editor(&diff_editor, &diff_edit_baton,
merge_b->ctx->wc_ctx, target_abspath,
depth,
merge_b->ra_session2, revision1,
FALSE, merge_b->dry_run,
&merge_callbacks, merge_b,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
notification_receiver, notify_b,
scratch_pool, scratch_pool));
SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1,
&reporter, &report_baton, revision2,
"", depth, merge_b->ignore_ancestry,
TRUE,
url2, diff_editor, diff_edit_baton, scratch_pool));
SVN_ERR(reporter->set_path(report_baton, "", target_start, depth,
FALSE, NULL, scratch_pool));
if (honor_mergeinfo && children_with_mergeinfo)
{
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
for (i = 1; i < children_with_mergeinfo->nelts; i++)
{
svn_merge_range_t *range;
const char *child_repos_path;
svn_client__merge_path_t *parent;
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_client__merge_path_t *);
int parent_index;
SVN_ERR_ASSERT(child);
if (child->absent)
continue;
svn_pool_clear(iterpool);
parent_index = find_nearest_ancestor(children_with_mergeinfo,
FALSE, child->abspath);
parent = APR_ARRAY_IDX(children_with_mergeinfo, parent_index,
svn_client__merge_path_t *);
if (child->remaining_ranges->nelts)
{
range = APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *);
if ((!is_rollback && range->start > revision2)
|| (is_rollback && range->start < revision2))
{
continue;
}
else if (parent->remaining_ranges->nelts)
{
svn_merge_range_t *parent_range =
APR_ARRAY_IDX(parent->remaining_ranges, 0,
svn_merge_range_t *);
svn_merge_range_t *child_range =
APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *);
if (parent_range->start == child_range->start)
continue;
}
}
else
{
if (parent->remaining_ranges->nelts == 0)
continue;
}
child_repos_path = svn_dirent_is_child(target_abspath,
child->abspath,
iterpool);
SVN_ERR_ASSERT(child_repos_path);
if ((child->remaining_ranges->nelts == 0)
|| (is_rollback && (range->start < revision2))
|| (!is_rollback && (range->start > revision2)))
{
SVN_ERR(reporter->set_path(report_baton, child_repos_path,
revision2, depth, FALSE,
NULL, iterpool));
}
else
{
SVN_ERR(reporter->set_path(report_baton, child_repos_path,
range->start, depth, FALSE,
NULL, iterpool));
}
}
svn_pool_destroy(iterpool);
}
SVN_ERR(reporter->finish_report(report_baton, scratch_pool));
if (old_sess2_url)
SVN_ERR(svn_ra_reparent(merge_b->ra_session2, old_sess2_url, scratch_pool));
*(merge_b->use_sleep) = TRUE;
return SVN_NO_ERROR;
}
static svn_revnum_t
get_most_inclusive_start_rev(const apr_array_header_t *children_with_mergeinfo,
svn_boolean_t is_rollback)
{
int i;
svn_revnum_t start_rev = SVN_INVALID_REVNUM;
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
svn_merge_range_t *range;
if ((! child) || child->absent)
continue;
if (! child->remaining_ranges->nelts)
continue;
range = APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
if ((i == 0) && (range->start == range->end))
continue;
if ((start_rev == SVN_INVALID_REVNUM)
|| (is_rollback && (range->start > start_rev))
|| ((! is_rollback) && (range->start < start_rev)))
start_rev = range->start;
}
return start_rev;
}
static svn_revnum_t
get_most_inclusive_end_rev(const apr_array_header_t *children_with_mergeinfo,
svn_boolean_t is_rollback)
{
int i;
svn_revnum_t end_rev = SVN_INVALID_REVNUM;
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
if (!child || child->absent)
continue;
if (child->remaining_ranges->nelts > 0)
{
svn_merge_range_t *range = APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *);
if ((end_rev == SVN_INVALID_REVNUM)
|| (is_rollback && (range->end > end_rev))
|| ((! is_rollback) && (range->end < end_rev)))
end_rev = range->end;
}
}
return end_rev;
}
static void
slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
svn_boolean_t is_rollback, svn_revnum_t end_rev,
apr_pool_t *pool)
{
int i;
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_client__merge_path_t *);
if (!child || child->absent)
continue;
if (child->remaining_ranges->nelts > 0)
{
svn_merge_range_t *range = APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *);
if ((is_rollback && (range->start > end_rev)
&& (range->end < end_rev))
|| (!is_rollback && (range->start < end_rev)
&& (range->end > end_rev)))
{
int j;
svn_merge_range_t *split_range1, *split_range2;
apr_array_header_t *orig_remaining_ranges =
child->remaining_ranges;
split_range1 = svn_merge_range_dup(range, pool);
split_range2 = svn_merge_range_dup(range, pool);
split_range1->end = end_rev;
split_range2->start = end_rev;
child->remaining_ranges =
apr_array_make(pool, (child->remaining_ranges->nelts + 1),
sizeof(svn_merge_range_t *));
APR_ARRAY_PUSH(child->remaining_ranges,
svn_merge_range_t *) = split_range1;
APR_ARRAY_PUSH(child->remaining_ranges,
svn_merge_range_t *) = split_range2;
for (j = 1; j < orig_remaining_ranges->nelts; j++)
{
svn_merge_range_t *orig_range =
APR_ARRAY_IDX(orig_remaining_ranges, j,
svn_merge_range_t *);
APR_ARRAY_PUSH(child->remaining_ranges,
svn_merge_range_t *) = orig_range;
}
}
}
}
}
static void
remove_first_range_from_remaining_ranges(svn_revnum_t revision,
apr_array_header_t
*children_with_mergeinfo,
apr_pool_t *pool)
{
int i, j;
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_client__merge_path_t *);
if (!child || child->absent)
continue;
if (child->remaining_ranges->nelts > 0)
{
svn_merge_range_t *first_range =
APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
if (first_range->end == revision)
{
apr_array_header_t *orig_remaining_ranges =
child->remaining_ranges;
child->remaining_ranges =
apr_array_make(pool, (child->remaining_ranges->nelts - 1),
sizeof(svn_merge_range_t *));
for (j = 1; j < orig_remaining_ranges->nelts; j++)
{
svn_merge_range_t *range =
APR_ARRAY_IDX(orig_remaining_ranges,
j,
svn_merge_range_t *);
APR_ARRAY_PUSH(child->remaining_ranges,
svn_merge_range_t *) = range;
}
}
}
}
}
static svn_error_t *
single_file_merge_get_file(const char **filename,
svn_ra_session_t *ra_session,
apr_hash_t **props,
svn_revnum_t rev,
const char *wc_target,
apr_pool_t *pool)
{
svn_stream_t *stream;
SVN_ERR(svn_stream_open_unique(&stream, filename,
svn_dirent_dirname(wc_target, pool),
svn_io_file_del_none, pool, pool));
SVN_ERR(svn_ra_get_file(ra_session, "", rev,
stream, NULL, props, pool));
return svn_stream_close(stream);
}
static APR_INLINE void
single_file_merge_notify(void *notify_baton,
const char *local_abspath,
svn_wc_notify_action_t action,
svn_wc_notify_state_t text_state,
svn_wc_notify_state_t prop_state,
svn_wc_notify_t *header_notification,
svn_boolean_t *header_sent,
apr_pool_t *pool)
{
svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, action, pool);
notify->kind = svn_node_file;
notify->content_state = text_state;
notify->prop_state = prop_state;
if (notify->content_state == svn_wc_notify_state_missing)
notify->action = svn_wc_notify_skip;
if (IS_OPERATIVE_NOTIFICATION(notify)
&& header_notification
&& (! *header_sent))
{
notification_receiver(notify_baton, header_notification, pool);
*header_sent = TRUE;
}
notification_receiver(notify_baton, notify, pool);
}
static int
compare_merge_path_t_as_paths(const void *a,
const void *b)
{
const svn_client__merge_path_t *child1
= *((const svn_client__merge_path_t * const *) a);
const svn_client__merge_path_t *child2
= *((const svn_client__merge_path_t * const *) b);
return svn_path_compare_paths(child1->abspath, child2->abspath);
}
static svn_client__merge_path_t *
get_child_with_mergeinfo(const apr_array_header_t *children_with_mergeinfo,
const char *abspath)
{
svn_client__merge_path_t merge_path;
svn_client__merge_path_t *key;
svn_client__merge_path_t **pchild;
merge_path.abspath = abspath;
key = &merge_path;
pchild = bsearch(&key, children_with_mergeinfo->elts,
children_with_mergeinfo->nelts,
children_with_mergeinfo->elt_size,
compare_merge_path_t_as_paths);
return pchild ? *pchild : NULL;
}
static void
insert_child_to_merge(apr_array_header_t *children_with_mergeinfo,
const svn_client__merge_path_t *insert_element,
apr_pool_t *pool)
{
int insert_index;
const svn_client__merge_path_t *new_element;
insert_index =
svn_sort__bsearch_lower_bound(&insert_element, children_with_mergeinfo,
compare_merge_path_t_as_paths);
new_element = svn_client__merge_path_dup(insert_element, pool);
svn_sort__array_insert(&new_element, children_with_mergeinfo, insert_index);
}
static svn_error_t *
insert_parent_and_sibs_of_sw_absent_del_subtree(
apr_array_header_t *children_with_mergeinfo,
merge_cmd_baton_t *merge_cmd_baton,
int *curr_index,
svn_client__merge_path_t *child,
svn_depth_t depth,
apr_pool_t *pool)
{
svn_client__merge_path_t *parent;
const char *parent_abspath;
apr_pool_t *iterpool;
const apr_array_header_t *children;
int i;
if (!(child->absent
|| (child->switched
&& strcmp(merge_cmd_baton->target_abspath,
child->abspath) != 0)))
return SVN_NO_ERROR;
parent_abspath = svn_dirent_dirname(child->abspath, pool);
parent = get_child_with_mergeinfo(children_with_mergeinfo, parent_abspath);
if (parent)
{
parent->missing_child = TRUE;
}
else
{
parent = apr_pcalloc(pool, sizeof(*parent));
parent->abspath = apr_pstrdup(pool, parent_abspath);
parent->missing_child = TRUE;
insert_child_to_merge(children_with_mergeinfo, parent, pool);
(*curr_index)++;
}
SVN_ERR(svn_wc__node_get_children(&children, merge_cmd_baton->ctx->wc_ctx,
parent_abspath, FALSE, pool, pool));
iterpool = svn_pool_create(pool);
for (i = 0; i < children->nelts; i++)
{
const char *child_abspath = APR_ARRAY_IDX(children, i, const char *);
svn_client__merge_path_t *sibling_of_missing;
svn_pool_clear(iterpool);
sibling_of_missing = get_child_with_mergeinfo(children_with_mergeinfo,
child_abspath);
if (!sibling_of_missing)
{
if (depth == svn_depth_files)
{
svn_node_kind_t child_kind;
SVN_ERR(svn_wc_read_kind(&child_kind,
merge_cmd_baton->ctx->wc_ctx,
child_abspath, FALSE, iterpool));
if (child_kind != svn_node_file)
continue;
}
sibling_of_missing = apr_pcalloc(pool,
sizeof(*sibling_of_missing));
sibling_of_missing->abspath = apr_pstrdup(pool,
child_abspath);
insert_child_to_merge(children_with_mergeinfo, sibling_of_missing,
pool);
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
struct pre_merge_status_baton_t
{
svn_wc_context_t *wc_ctx;
apr_hash_t *shallow_subtrees;
apr_hash_t *missing_subtrees;
apr_hash_t *switched_subtrees;
apr_pool_t *pool;
};
static svn_error_t *
pre_merge_status_cb(void *baton,
const char *local_abspath,
const svn_wc_status3_t *status,
apr_pool_t *pool)
{
struct pre_merge_status_baton_t *pmsb = baton;
const char *dup_abspath = NULL;
svn_boolean_t is_file_external = FALSE;
if (status->versioned
&& status->switched
&& status->kind == svn_node_file)
{
svn_node_kind_t external_kind;
SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL,
NULL,
pmsb->wc_ctx, local_abspath,
local_abspath, TRUE, pool, pool));
is_file_external = (external_kind == svn_node_file);
}
if (status->switched && !is_file_external)
{
if (!dup_abspath)
dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
apr_hash_set(pmsb->switched_subtrees,
apr_pstrdup(pmsb->pool, local_abspath),
APR_HASH_KEY_STRING,
dup_abspath);
}
if (status->depth == svn_depth_empty
|| status->depth == svn_depth_files)
{
svn_depth_t *depth = apr_pcalloc(pmsb->pool, sizeof *depth);
if (!dup_abspath)
dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
*depth = status->depth;
apr_hash_set(pmsb->shallow_subtrees,
dup_abspath,
APR_HASH_KEY_STRING,
depth);
}
if (status->node_status == svn_wc_status_missing)
{
svn_boolean_t new_missing_root = TRUE;
apr_hash_index_t *hi;
if (!dup_abspath)
dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
for (hi = apr_hash_first(pool, pmsb->missing_subtrees);
hi;
hi = apr_hash_next(hi))
{
const char *missing_root_path = svn__apr_hash_index_key(hi);
if (svn_dirent_is_ancestor(missing_root_path,
dup_abspath))
{
new_missing_root = FALSE;
break;
}
}
if (new_missing_root)
apr_hash_set(pmsb->missing_subtrees, dup_abspath,
APR_HASH_KEY_STRING, dup_abspath);
}
return SVN_NO_ERROR;
}
static svn_error_t *
get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
merge_cmd_baton_t *merge_cmd_baton,
const char* merge_src_canon_path,
const char *source_root_url,
const char *url1,
const char *url2,
svn_revnum_t revision1,
svn_revnum_t revision2,
svn_ra_session_t *ra_session,
svn_depth_t depth,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
apr_pool_t *iterpool = NULL;
apr_hash_t *subtrees_with_mergeinfo;
apr_hash_t *server_excluded_subtrees;
apr_hash_t *switched_subtrees;
apr_hash_t *shallow_subtrees;
apr_hash_t *missing_subtrees;
struct pre_merge_status_baton_t pre_merge_status_baton;
svn_opt_revision_t working_revision;
working_revision.kind = svn_opt_revision_working;
SVN_ERR(svn_client_propget4(&subtrees_with_mergeinfo, SVN_PROP_MERGEINFO,
merge_cmd_baton->target_abspath,
&working_revision,
&working_revision, NULL, depth, NULL,
merge_cmd_baton->ctx, scratch_pool,
scratch_pool));
if (subtrees_with_mergeinfo)
{
apr_hash_index_t *hi;
if (!iterpool)
iterpool = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
hi;
hi = apr_hash_next(hi))
{
svn_error_t *err;
svn_mergeinfo_t child_pre_merge_mergeinfo;
const char *wc_path = svn__apr_hash_index_key(hi);
svn_string_t *mergeinfo_string = svn__apr_hash_index_val(hi);
svn_client__merge_path_t *mergeinfo_child =
apr_pcalloc(result_pool, sizeof(*mergeinfo_child));
svn_pool_clear(iterpool);
mergeinfo_child->abspath = apr_pstrdup(result_pool, wc_path);
err = svn_mergeinfo_parse(&child_pre_merge_mergeinfo,
mergeinfo_string->data, result_pool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
err = svn_error_createf(
SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
_("Invalid mergeinfo detected on '%s', "
"mergetracking not possible"),
svn_dirent_local_style(wc_path, scratch_pool));
}
return svn_error_trace(err);
}
mergeinfo_child->pre_merge_mergeinfo = child_pre_merge_mergeinfo;
SVN_ERR(svn_mergeinfo__string_has_noninheritable(
&(mergeinfo_child->has_noninheritable), mergeinfo_string->data,
iterpool));
insert_child_to_merge(children_with_mergeinfo, mergeinfo_child,
result_pool);
}
qsort(children_with_mergeinfo->elts,
children_with_mergeinfo->nelts,
children_with_mergeinfo->elt_size,
compare_merge_path_t_as_paths);
}
pre_merge_status_baton.wc_ctx = merge_cmd_baton->ctx->wc_ctx;
switched_subtrees = apr_hash_make(scratch_pool);
pre_merge_status_baton.switched_subtrees = switched_subtrees;
shallow_subtrees = apr_hash_make(scratch_pool);
pre_merge_status_baton.shallow_subtrees = shallow_subtrees;
missing_subtrees = apr_hash_make(scratch_pool);
pre_merge_status_baton.missing_subtrees = missing_subtrees;
pre_merge_status_baton.pool = scratch_pool;
SVN_ERR(svn_wc_walk_status(merge_cmd_baton->ctx->wc_ctx,
merge_cmd_baton->target_abspath,
depth, TRUE, TRUE, TRUE, NULL,
pre_merge_status_cb,
&pre_merge_status_baton,
merge_cmd_baton->ctx->cancel_func,
merge_cmd_baton->ctx->cancel_baton,
scratch_pool));
if (apr_hash_count(missing_subtrees))
{
apr_hash_index_t *hi;
svn_stringbuf_t *missing_subtree_err_buf =
svn_stringbuf_create(_("Merge tracking not allowed with missing "
"subtrees; try restoring these items "
"first:\n"), scratch_pool);
iterpool = svn_pool_create(scratch_pool);
for (hi = apr_hash_first(scratch_pool, missing_subtrees);
hi;
hi = apr_hash_next(hi))
{
svn_pool_clear(iterpool);
svn_stringbuf_appendcstr(missing_subtree_err_buf,
svn_dirent_local_style(
svn__apr_hash_index_key(hi), iterpool));
svn_stringbuf_appendcstr(missing_subtree_err_buf, "\n");
}
return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
NULL, missing_subtree_err_buf->data);
}
if (apr_hash_count(switched_subtrees))
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, switched_subtrees);
hi;
hi = apr_hash_next(hi))
{
const char *wc_path = svn__apr_hash_index_key(hi);
svn_client__merge_path_t *child = get_child_with_mergeinfo(
children_with_mergeinfo, wc_path);
if (child)
{
child->switched = TRUE;
}
else
{
svn_client__merge_path_t *switched_child =
apr_pcalloc(result_pool, sizeof(*switched_child));
switched_child->abspath = apr_pstrdup(result_pool, wc_path);
switched_child->switched = TRUE;
insert_child_to_merge(children_with_mergeinfo, switched_child,
result_pool);
}
}
}
if (apr_hash_count(shallow_subtrees))
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, shallow_subtrees);
hi;
hi = apr_hash_next(hi))
{
svn_boolean_t new_shallow_child = FALSE;
const char *wc_path = svn__apr_hash_index_key(hi);
svn_depth_t *child_depth = svn__apr_hash_index_val(hi);
svn_client__merge_path_t *shallow_child = get_child_with_mergeinfo(
children_with_mergeinfo, wc_path);
if (shallow_child)
{
if (*child_depth == svn_depth_empty
|| *child_depth == svn_depth_files)
shallow_child->missing_child = TRUE;
}
else
{
shallow_child = apr_pcalloc(result_pool,
sizeof(*shallow_child));
new_shallow_child = TRUE;
shallow_child->abspath = apr_pstrdup(result_pool, wc_path);
if (*child_depth == svn_depth_empty
|| *child_depth == svn_depth_files)
shallow_child->missing_child = TRUE;
}
if (!shallow_child->has_noninheritable
&& (*child_depth == svn_depth_empty
|| *child_depth == svn_depth_files))
{
shallow_child->has_noninheritable = TRUE;
}
if (new_shallow_child)
insert_child_to_merge(children_with_mergeinfo, shallow_child,
result_pool);
}
}
SVN_ERR(svn_wc__get_server_excluded_subtrees(&server_excluded_subtrees,
merge_cmd_baton->ctx->wc_ctx,
merge_cmd_baton->target_abspath,
result_pool, scratch_pool));
if (server_excluded_subtrees)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, server_excluded_subtrees);
hi;
hi = apr_hash_next(hi))
{
const char *wc_path = svn__apr_hash_index_key(hi);
svn_client__merge_path_t *child = get_child_with_mergeinfo(
children_with_mergeinfo, wc_path);
if (child)
{
child->absent = TRUE;
}
else
{
svn_client__merge_path_t *absent_child =
apr_pcalloc(result_pool, sizeof(*absent_child));
absent_child->abspath = apr_pstrdup(result_pool, wc_path);
absent_child->absent = TRUE;
insert_child_to_merge(children_with_mergeinfo, absent_child,
result_pool);
}
}
}
if (!get_child_with_mergeinfo(children_with_mergeinfo,
merge_cmd_baton->target_abspath))
{
svn_client__merge_path_t *target_child =
apr_pcalloc(result_pool, sizeof(*target_child));
target_child->abspath = apr_pstrdup(result_pool,
merge_cmd_baton->target_abspath);
insert_child_to_merge(children_with_mergeinfo, target_child,
result_pool);
}
if (depth == svn_depth_immediates || depth == svn_depth_files)
{
int j;
const apr_array_header_t *immediate_children;
SVN_ERR(svn_wc__node_get_children_of_working_node(
&immediate_children, merge_cmd_baton->ctx->wc_ctx,
merge_cmd_baton->target_abspath, FALSE, scratch_pool, scratch_pool));
if (!iterpool)
iterpool = svn_pool_create(scratch_pool);
for (j = 0; j < immediate_children->nelts; j++)
{
const char *immediate_child_abspath =
APR_ARRAY_IDX(immediate_children, j, const char *);
svn_node_kind_t immediate_child_kind;
svn_pool_clear(iterpool);
SVN_ERR(svn_wc_read_kind(&immediate_child_kind,
merge_cmd_baton->ctx->wc_ctx,
immediate_child_abspath, FALSE,
iterpool));
if ((immediate_child_kind == svn_node_dir
&& depth == svn_depth_immediates)
|| (immediate_child_kind == svn_node_file
&& depth == svn_depth_files))
{
if (!get_child_with_mergeinfo(children_with_mergeinfo,
immediate_child_abspath))
{
svn_client__merge_path_t *immediate_child =
apr_pcalloc(result_pool, sizeof(*immediate_child));
immediate_child->abspath =
apr_pstrdup(result_pool, immediate_child_abspath);
if (immediate_child_kind == svn_node_dir
&& depth == svn_depth_immediates)
immediate_child->immediate_child_dir = TRUE;
insert_child_to_merge(children_with_mergeinfo,
immediate_child, result_pool);
}
}
}
}
if (depth <= svn_depth_empty)
return SVN_NO_ERROR;
iterpool = svn_pool_create(scratch_pool);
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i,
svn_client__merge_path_t *);
svn_pool_clear(iterpool);
if (child->has_noninheritable)
{
const apr_array_header_t *children;
int j;
SVN_ERR(svn_wc__node_get_children(&children,
merge_cmd_baton->ctx->wc_ctx,
child->abspath, FALSE,
iterpool, iterpool));
for (j = 0; j < children->nelts; j++)
{
svn_client__merge_path_t *child_of_noninheritable;
const char *child_abspath = APR_ARRAY_IDX(children, j,
const char*);
child_of_noninheritable =
get_child_with_mergeinfo(children_with_mergeinfo,
child_abspath);
if (!child_of_noninheritable)
{
if (depth == svn_depth_files)
{
svn_node_kind_t child_kind;
SVN_ERR(svn_wc_read_kind(&child_kind,
merge_cmd_baton->ctx->wc_ctx,
child_abspath, FALSE,
iterpool));
if (child_kind != svn_node_file)
continue;
}
child_of_noninheritable =
apr_pcalloc(result_pool,
sizeof(*child_of_noninheritable));
child_of_noninheritable->child_of_noninheritable = TRUE;
child_of_noninheritable->abspath =
apr_pstrdup(result_pool,
child_abspath);
insert_child_to_merge(children_with_mergeinfo,
child_of_noninheritable,
result_pool);
if (!merge_cmd_baton->dry_run
&& merge_cmd_baton->same_repos)
{
svn_boolean_t inherited;
svn_mergeinfo_t mergeinfo;
SVN_ERR(svn_client__get_wc_mergeinfo(
&mergeinfo, &inherited,
svn_mergeinfo_nearest_ancestor,
child_of_noninheritable->abspath,
merge_cmd_baton->target_abspath, NULL, FALSE,
merge_cmd_baton->ctx, iterpool, iterpool));
SVN_ERR(svn_client__record_wc_mergeinfo(
child_of_noninheritable->abspath, mergeinfo,
FALSE, merge_cmd_baton->ctx, iterpool));
}
}
}
}
SVN_ERR(insert_parent_and_sibs_of_sw_absent_del_subtree(
children_with_mergeinfo, merge_cmd_baton, &i, child,
depth, result_pool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
log_changed_revs(void *baton,
svn_log_entry_t *log_entry,
apr_pool_t *pool)
{
apr_array_header_t *revs = baton;
svn_revnum_t *revision = apr_palloc(revs->pool, sizeof(*revision));
*revision = log_entry->revision;
APR_ARRAY_PUSH(revs, svn_revnum_t *) = revision;
return SVN_NO_ERROR;
}
static svn_error_t *
remove_noop_merge_ranges(apr_array_header_t **operative_ranges_p,
svn_ra_session_t *ra_session,
const apr_array_header_t *ranges,
apr_pool_t *pool)
{
int i;
svn_revnum_t oldest_rev = SVN_INVALID_REVNUM;
svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
apr_array_header_t *changed_revs =
apr_array_make(pool, ranges->nelts, sizeof(svn_revnum_t *));
apr_array_header_t *operative_ranges =
apr_array_make(ranges->pool, ranges->nelts, ranges->elt_size);
apr_array_header_t *log_targets =
apr_array_make(pool, 1, sizeof(const char *));
APR_ARRAY_PUSH(log_targets, const char *) = "";
for (i = 0; i < ranges->nelts; i++)
{
svn_merge_range_t *r = APR_ARRAY_IDX(ranges, i, svn_merge_range_t *);
svn_revnum_t max_rev = MAX(r->start, r->end);
svn_revnum_t min_rev = MIN(r->start, r->end) + 1;
if ((! SVN_IS_VALID_REVNUM(youngest_rev)) || (max_rev > youngest_rev))
youngest_rev = max_rev;
if ((! SVN_IS_VALID_REVNUM(oldest_rev)) || (min_rev < oldest_rev))
oldest_rev = min_rev;
}
SVN_ERR(svn_ra_get_log2(ra_session, log_targets, youngest_rev,
oldest_rev, 0, FALSE, FALSE, FALSE,
apr_array_make(pool, 0, sizeof(const char *)),
log_changed_revs, changed_revs, pool));
if (changed_revs->nelts)
{
svn_revnum_t oldest_changed_rev, youngest_changed_rev;
youngest_changed_rev = *(APR_ARRAY_IDX(changed_revs,
0, svn_revnum_t *));
oldest_changed_rev = *(APR_ARRAY_IDX(changed_revs,
changed_revs->nelts - 1,
svn_revnum_t *));
for (i = 0; i < ranges->nelts; i++)
{
svn_merge_range_t *range = APR_ARRAY_IDX(ranges, i,
svn_merge_range_t *);
svn_revnum_t range_min = MIN(range->start, range->end) + 1;
svn_revnum_t range_max = MAX(range->start, range->end);
int j;
if ((range_min > youngest_changed_rev)
|| (range_max < oldest_changed_rev))
continue;
for (j = 0; j < changed_revs->nelts; j++)
{
svn_revnum_t *changed_rev =
APR_ARRAY_IDX(changed_revs, j, svn_revnum_t *);
if ((*changed_rev >= range_min) && (*changed_rev <= range_max))
{
APR_ARRAY_PUSH(operative_ranges, svn_merge_range_t *) =
range;
break;
}
}
}
}
*operative_ranges_p = operative_ranges;
return SVN_NO_ERROR;
}
static int
compare_merge_source_ts(const void *a,
const void *b)
{
svn_revnum_t a_rev = (*(const merge_source_t *const *)a)->rev1;
svn_revnum_t b_rev = (*(const merge_source_t *const *)b)->rev1;
if (a_rev == b_rev)
return 0;
return a_rev < b_rev ? 1 : -1;
}
static svn_error_t *
combine_range_with_segments(apr_array_header_t **merge_source_ts_p,
svn_merge_range_t *range,
const apr_array_header_t *segments,
const char *source_root_url,
apr_pool_t *pool)
{
apr_array_header_t *merge_source_ts =
apr_array_make(pool, 1, sizeof(merge_source_t *));
svn_revnum_t minrev = MIN(range->start, range->end) + 1;
svn_revnum_t maxrev = MAX(range->start, range->end);
svn_boolean_t subtractive = (range->start > range->end);
int i;
for (i = 0; i < segments->nelts; i++)
{
svn_location_segment_t *segment =
APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
merge_source_t *merge_source;
const char *path1 = NULL;
svn_revnum_t rev1;
if ((segment->range_end < minrev)
|| (segment->range_start > maxrev)
|| (! segment->path))
continue;
rev1 = MAX(segment->range_start, minrev) - 1;
if (minrev <= segment->range_start)
{
if (i > 0)
{
path1 = (APR_ARRAY_IDX(segments, i - 1,
svn_location_segment_t *))->path;
}
if ((! path1) && (i > 1))
{
path1 = (APR_ARRAY_IDX(segments, i - 2,
svn_location_segment_t *))->path;
rev1 = (APR_ARRAY_IDX(segments, i - 2,
svn_location_segment_t *))->range_end;
}
}
else
{
path1 = apr_pstrdup(pool, segment->path);
}
if (! (path1 && segment->path))
continue;
merge_source = apr_pcalloc(pool, sizeof(*merge_source));
merge_source->url1 = svn_path_url_add_component2(source_root_url,
path1,
pool);
merge_source->url2 = svn_path_url_add_component2(source_root_url,
segment->path,
pool);
merge_source->rev1 = rev1;
merge_source->rev2 = MIN(segment->range_end, maxrev);
if (subtractive)
{
svn_revnum_t tmprev = merge_source->rev1;
const char *tmpurl = merge_source->url1;
merge_source->rev1 = merge_source->rev2;
merge_source->url1 = merge_source->url2;
merge_source->rev2 = tmprev;
merge_source->url2 = tmpurl;
}
APR_ARRAY_PUSH(merge_source_ts, merge_source_t *) = merge_source;
}
if (subtractive && (merge_source_ts->nelts > 1))
qsort(merge_source_ts->elts, merge_source_ts->nelts,
merge_source_ts->elt_size, compare_merge_source_ts);
*merge_source_ts_p = merge_source_ts;
return SVN_NO_ERROR;
}
static svn_error_t *
normalize_merge_sources(apr_array_header_t **merge_sources_p,
const char *source,
const char *source_url,
const char *source_root_url,
const svn_opt_revision_t *peg_revision,
const apr_array_header_t *ranges_to_merge,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
svn_revnum_t peg_revnum;
svn_revnum_t oldest_requested = SVN_INVALID_REVNUM;
svn_revnum_t youngest_requested = SVN_INVALID_REVNUM;
svn_revnum_t trim_revision = SVN_INVALID_REVNUM;
apr_array_header_t *merge_range_ts, *segments;
const char *source_abspath_or_url;
int i;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
if(!svn_path_is_url(source))
SVN_ERR(svn_dirent_get_absolute(&source_abspath_or_url, source,
scratch_pool));
else
source_abspath_or_url = source;
*merge_sources_p = apr_array_make(result_pool, 1, sizeof(merge_source_t *));
SVN_ERR(svn_client__get_revision_number(&peg_revnum, &youngest_rev,
ctx->wc_ctx,
source_abspath_or_url,
ra_session, peg_revision,
iterpool));
if (! SVN_IS_VALID_REVNUM(peg_revnum))
return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
merge_range_ts = apr_array_make(scratch_pool, ranges_to_merge->nelts,
sizeof(svn_merge_range_t *));
for (i = 0; i < ranges_to_merge->nelts; i++)
{
svn_revnum_t range_start_rev, range_end_rev;
svn_opt_revision_t *range_start =
&((APR_ARRAY_IDX(ranges_to_merge, i,
svn_opt_revision_range_t *))->start);
svn_opt_revision_t *range_end =
&((APR_ARRAY_IDX(ranges_to_merge, i,
svn_opt_revision_range_t *))->end);
svn_pool_clear(iterpool);
if ((range_start->kind == svn_opt_revision_unspecified)
|| (range_end->kind == svn_opt_revision_unspecified))
return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
_("Not all required revisions are specified"));
SVN_ERR(svn_client__get_revision_number(&range_start_rev, &youngest_rev,
ctx->wc_ctx,
source_abspath_or_url,
ra_session, range_start,
iterpool));
SVN_ERR(svn_client__get_revision_number(&range_end_rev, &youngest_rev,
ctx->wc_ctx,
source_abspath_or_url,
ra_session, range_end,
iterpool));
if (range_start_rev != range_end_rev)
{
svn_merge_range_t *range = apr_pcalloc(scratch_pool,
sizeof(*range));
range->start = range_start_rev;
range->end = range_end_rev;
range->inheritable = TRUE;
APR_ARRAY_PUSH(merge_range_ts, svn_merge_range_t *) = range;
}
}
if (merge_range_ts->nelts == 0)
{
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
for (i = 0; i < merge_range_ts->nelts; i++)
{
svn_merge_range_t *range =
APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *);
svn_revnum_t minrev = MIN(range->start, range->end);
svn_revnum_t maxrev = MAX(range->start, range->end);
if ((! SVN_IS_VALID_REVNUM(oldest_requested))
|| (minrev < oldest_requested))
oldest_requested = minrev;
if ((! SVN_IS_VALID_REVNUM(youngest_requested))
|| (maxrev > youngest_requested))
youngest_requested = maxrev;
}
if (peg_revnum < youngest_requested)
{
const char *start_url;
svn_opt_revision_t requested, unspec, pegrev, *start_revision;
unspec.kind = svn_opt_revision_unspecified;
requested.kind = svn_opt_revision_number;
requested.value.number = youngest_requested;
pegrev.kind = svn_opt_revision_number;
pegrev.value.number = peg_revnum;
SVN_ERR(svn_client__repos_locations(&start_url, &start_revision,
NULL, NULL,
ra_session, source_url,
&pegrev, &requested,
&unspec, ctx, iterpool));
peg_revnum = youngest_requested;
}
SVN_ERR(svn_client__repos_location_segments(&segments,
ra_session, "",
peg_revnum,
youngest_requested,
oldest_requested,
ctx, result_pool));
trim_revision = SVN_INVALID_REVNUM;
if (segments->nelts)
{
svn_location_segment_t *segment =
APR_ARRAY_IDX(segments, 0, svn_location_segment_t *);
if (segment->range_start != oldest_requested)
{
trim_revision = segment->range_start;
}
else if (! segment->path)
{
if (segments->nelts > 1)
{
svn_location_segment_t *segment2 =
APR_ARRAY_IDX(segments, 1, svn_location_segment_t *);
const char *copyfrom_path, *segment_url;
svn_revnum_t copyfrom_rev;
svn_opt_revision_t range_start_rev;
range_start_rev.kind = svn_opt_revision_number;
range_start_rev.value.number = segment2->range_start;
segment_url = svn_path_url_add_component2(source_root_url,
segment2->path,
iterpool);
SVN_ERR(svn_client__get_copy_source(segment_url,
&range_start_rev,
©from_path,
©from_rev,
ctx, result_pool));
if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev))
{
svn_location_segment_t *new_segment =
apr_pcalloc(result_pool, sizeof(*new_segment));
new_segment->path = (*copyfrom_path == '/')
? copyfrom_path + 1 : copyfrom_path;
new_segment->range_start = copyfrom_rev;
new_segment->range_end = copyfrom_rev;
segment->range_start = copyfrom_rev + 1;
APR_ARRAY_PUSH(segments, svn_location_segment_t *) = NULL;
memmove(segments->elts + segments->elt_size,
segments->elts,
segments->elt_size * (segments->nelts - 1));
APR_ARRAY_IDX(segments, 0, svn_location_segment_t *) =
new_segment;
}
}
}
}
for (i = 0; i < merge_range_ts->nelts; i++)
{
svn_merge_range_t *range =
APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *);
apr_array_header_t *merge_sources;
int j;
if (SVN_IS_VALID_REVNUM(trim_revision))
{
if (MAX(range->start, range->end) < trim_revision)
continue;
if (range->start < trim_revision)
range->start = trim_revision;
if (range->end < trim_revision)
range->end = trim_revision;
}
SVN_ERR(combine_range_with_segments(&merge_sources, range,
segments, source_root_url,
result_pool));
for (j = 0; j < merge_sources->nelts; j++)
{
APR_ARRAY_PUSH(*merge_sources_p, merge_source_t *) =
APR_ARRAY_IDX(merge_sources, j, merge_source_t *);
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
filter_natural_history_from_mergeinfo(apr_array_header_t **filtered_rangelist,
const char *source_rel_path,
svn_mergeinfo_t implicit_mergeinfo,
svn_merge_range_t *requested_range,
apr_pool_t *pool)
{
apr_array_header_t *requested_rangelist =
svn_rangelist__initialize(requested_range->start, requested_range->end,
requested_range->inheritable, pool);
*filtered_rangelist = NULL;
if (implicit_mergeinfo
&& (requested_range->start < requested_range->end))
{
apr_array_header_t *implied_rangelist =
apr_hash_get(implicit_mergeinfo, source_rel_path,
APR_HASH_KEY_STRING);
if (implied_rangelist)
SVN_ERR(svn_rangelist_remove(filtered_rangelist,
implied_rangelist,
requested_rangelist,
FALSE, pool));
}
if (! (*filtered_rangelist))
*filtered_rangelist = requested_rangelist;
return SVN_NO_ERROR;
}
static svn_error_t *
do_file_merge(svn_mergeinfo_catalog_t result_catalog,
const char *url1,
svn_revnum_t revision1,
const char *url2,
svn_revnum_t revision2,
const char *target_abspath,
svn_boolean_t sources_related,
svn_boolean_t squelch_mergeinfo_notifications,
notification_receiver_baton_t *notify_b,
svn_boolean_t abort_on_conflicts,
merge_cmd_baton_t *merge_b,
apr_pool_t *scratch_pool)
{
apr_hash_t *props1, *props2;
const char *tmpfile1, *tmpfile2;
const char *mimetype1, *mimetype2;
svn_string_t *pval;
apr_array_header_t *propchanges, *remaining_ranges;
svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
svn_wc_notify_state_t text_state = svn_wc_notify_state_unknown;
svn_boolean_t tree_conflicted = FALSE;
svn_client_ctx_t *ctx = merge_b->ctx;
const char *mergeinfo_path;
svn_merge_range_t range;
svn_mergeinfo_t target_mergeinfo;
svn_merge_range_t *conflicted_range = NULL;
svn_boolean_t inherited = FALSE;
svn_boolean_t is_rollback = (revision1 > revision2);
const char *primary_url = is_rollback ? url1 : url2;
const char *target_url;
svn_boolean_t honor_mergeinfo, record_mergeinfo;
svn_client__merge_path_t *merge_target = NULL;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
honor_mergeinfo = HONOR_MERGEINFO(merge_b);
record_mergeinfo = RECORD_MERGEINFO(merge_b);
notify_b->is_single_file_merge = TRUE;
SVN_ERR(svn_wc__node_get_url(&target_url, merge_b->ctx->wc_ctx,
target_abspath,
iterpool, iterpool));
range.start = revision1;
range.end = revision2;
range.inheritable = TRUE;
if (honor_mergeinfo)
{
svn_error_t *err;
const char *source_root_url;
merge_target = apr_pcalloc(scratch_pool, sizeof(*merge_target));
merge_target->abspath = target_abspath;
SVN_ERR(svn_ra_get_repos_root2(merge_b->ra_session1,
&source_root_url, iterpool));
SVN_ERR(svn_client__path_relative_to_root(&mergeinfo_path,
merge_b->ctx->wc_ctx,
primary_url,
source_root_url, TRUE, NULL,
scratch_pool, iterpool));
SVN_ERR(svn_ra_reparent(merge_b->ra_session1, target_url,
iterpool));
err = get_full_mergeinfo(&target_mergeinfo,
&(merge_target->implicit_mergeinfo),
&inherited, svn_mergeinfo_inherited,
merge_b->ra_session1, target_abspath,
MAX(revision1, revision2),
MIN(revision1, revision2),
ctx, scratch_pool, iterpool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
err = svn_error_createf(
SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
_("Invalid mergeinfo detected on merge target '%s', "
"mergetracking not possible"),
svn_dirent_local_style(target_abspath, scratch_pool));
}
return svn_error_trace(err);
}
SVN_ERR(svn_ra_reparent(merge_b->ra_session1, url1, iterpool));
if (!merge_b->record_only)
{
SVN_ERR(calculate_remaining_ranges(NULL, merge_target,
source_root_url,
url1, revision1, url2, revision2,
target_mergeinfo,
merge_b->implicit_src_gap, FALSE,
merge_b->ra_session1,
ctx, scratch_pool,
iterpool));
remaining_ranges = merge_target->remaining_ranges;
}
}
if (!honor_mergeinfo || merge_b->record_only)
{
remaining_ranges = apr_array_make(scratch_pool, 1, sizeof(&range));
APR_ARRAY_PUSH(remaining_ranges, svn_merge_range_t *) = ⦥
}
if (!merge_b->record_only)
{
apr_array_header_t *ranges_to_merge = remaining_ranges;
int i;
if (merge_b->sources_ancestral && (remaining_ranges->nelts > 1))
{
const char *old_sess_url = NULL;
SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url,
merge_b->ra_session1,
primary_url,
iterpool));
SVN_ERR(remove_noop_merge_ranges(&ranges_to_merge,
merge_b->ra_session1,
remaining_ranges, scratch_pool));
if (old_sess_url)
SVN_ERR(svn_ra_reparent(merge_b->ra_session1, old_sess_url,
iterpool));
}
for (i = 0; i < ranges_to_merge->nelts; i++)
{
svn_wc_notify_t *n;
svn_boolean_t header_sent = FALSE;
svn_ra_session_t *ra_session1, *ra_session2;
svn_merge_range_t *r = APR_ARRAY_IDX(ranges_to_merge, i,
svn_merge_range_t *);
svn_pool_clear(iterpool);
n = svn_wc_create_notify(target_abspath,
merge_b->same_repos
? svn_wc_notify_merge_begin
: svn_wc_notify_foreign_merge_begin,
iterpool);
if (merge_b->sources_ancestral)
n->merge_range = r;
ra_session1 = merge_b->ra_session1;
ra_session2 = merge_b->ra_session2;
if (honor_mergeinfo && strcmp(url1, url2) != 0)
{
if (!is_rollback && r->start != revision1)
ra_session1 = ra_session2;
else if (is_rollback && r->end != revision2)
ra_session2 = ra_session1;
}
SVN_ERR(single_file_merge_get_file(&tmpfile1, ra_session1,
&props1, r->start,
target_abspath, iterpool));
SVN_ERR(single_file_merge_get_file(&tmpfile2, ra_session2,
&props2, r->end, target_abspath,
iterpool));
pval = apr_hash_get(props1, SVN_PROP_MIME_TYPE,
strlen(SVN_PROP_MIME_TYPE));
mimetype1 = pval ? pval->data : NULL;
pval = apr_hash_get(props2, SVN_PROP_MIME_TYPE,
strlen(SVN_PROP_MIME_TYPE));
mimetype2 = pval ? pval->data : NULL;
SVN_ERR(svn_prop_diffs(&propchanges, props2, props1, iterpool));
if (! (merge_b->ignore_ancestry || sources_related))
{
SVN_ERR(merge_file_deleted(&text_state,
&tree_conflicted,
target_abspath,
tmpfile1,
tmpfile2,
mimetype1, mimetype2,
props1,
merge_b,
iterpool));
single_file_merge_notify(notify_b, target_abspath,
tree_conflicted
? svn_wc_notify_tree_conflict
: svn_wc_notify_update_delete,
text_state,
svn_wc_notify_state_unknown,
n, &header_sent, iterpool);
SVN_ERR(merge_file_added(&text_state, &prop_state,
&tree_conflicted,
target_abspath,
tmpfile1,
tmpfile2,
r->start,
r->end,
mimetype1, mimetype2,
NULL, SVN_INVALID_REVNUM,
propchanges, props1,
merge_b,
iterpool));
single_file_merge_notify(notify_b, target_abspath,
tree_conflicted
? svn_wc_notify_tree_conflict
: svn_wc_notify_update_add,
text_state, prop_state, n,
&header_sent, iterpool);
}
else
{
SVN_ERR(merge_file_changed(&text_state, &prop_state,
&tree_conflicted,
target_abspath,
tmpfile1,
tmpfile2,
r->start,
r->end,
mimetype1, mimetype2,
propchanges, props1,
merge_b,
iterpool));
single_file_merge_notify(notify_b, target_abspath,
tree_conflicted
? svn_wc_notify_tree_conflict
: svn_wc_notify_update_update,
text_state, prop_state, n,
&header_sent, iterpool);
}
SVN_ERR(svn_io_remove_file2(tmpfile1, TRUE, iterpool));
SVN_ERR(svn_io_remove_file2(tmpfile2, TRUE, iterpool));
if ((i < (ranges_to_merge->nelts - 1) || abort_on_conflicts)
&& is_path_conflicted_by_merge(merge_b))
{
conflicted_range = svn_merge_range_dup(r, scratch_pool);
range.end = r->end;
break;
}
}
}
if (record_mergeinfo && remaining_ranges->nelts)
{
apr_array_header_t *filtered_rangelist;
SVN_ERR(filter_natural_history_from_mergeinfo(
&filtered_rangelist,
mergeinfo_path,
merge_target->implicit_mergeinfo,
&range,
iterpool));
if (filtered_rangelist->nelts
&& (!notify_b->skipped_abspaths
|| (apr_hash_count(notify_b->skipped_abspaths) == 0)))
{
apr_hash_t *merges = apr_hash_make(iterpool);
if (inherited)
SVN_ERR(svn_client__record_wc_mergeinfo(target_abspath,
target_mergeinfo,
FALSE, ctx,
iterpool));
apr_hash_set(merges, target_abspath, APR_HASH_KEY_STRING,
filtered_rangelist);
if (!squelch_mergeinfo_notifications)
{
svn_wc_notify_t *notify = svn_wc_create_notify(
target_abspath, svn_wc_notify_merge_record_info_begin,
iterpool);
svn_revnum_t youngest_rev;
svn_revnum_t oldest_rev;
SVN_ERR(svn_mergeinfo__get_range_endpoints(&youngest_rev,
&oldest_rev,
merges,
iterpool));
notify->merge_range = apr_pcalloc(iterpool,
sizeof(svn_merge_range_t));
notify->merge_range->start = oldest_rev;
notify->merge_range->end = youngest_rev;
notify->merge_range->inheritable = TRUE;
merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2,
notify, iterpool);
}
SVN_ERR(update_wc_mergeinfo(result_catalog, target_abspath,
mergeinfo_path, merges, is_rollback,
ctx, iterpool));
}
}
*(merge_b->use_sleep) = TRUE;
svn_pool_destroy(iterpool);
if (conflicted_range)
return make_merge_conflict_error(target_abspath, conflicted_range,
scratch_pool);
return SVN_NO_ERROR;
}
static svn_error_t *
process_children_with_new_mergeinfo(merge_cmd_baton_t *merge_b,
notification_receiver_baton_t *notify_b,
apr_pool_t *pool)
{
apr_pool_t *iterpool;
apr_hash_index_t *hi;
if (!merge_b->paths_with_new_mergeinfo || merge_b->dry_run)
return SVN_NO_ERROR;
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, merge_b->paths_with_new_mergeinfo);
hi;
hi = apr_hash_next(hi))
{
const char *abspath_with_new_mergeinfo = svn__apr_hash_index_key(hi);
const char *old_session_url = NULL;
const char *path_url;
svn_mergeinfo_t path_inherited_mergeinfo;
svn_mergeinfo_t path_explicit_mergeinfo;
svn_boolean_t inherited;
svn_client__merge_path_t *new_child;
apr_pool_clear(iterpool);
SVN_ERR(svn_wc__node_get_url(&path_url, merge_b->ctx->wc_ctx,
abspath_with_new_mergeinfo,
pool, pool));
SVN_ERR(svn_client__get_wc_mergeinfo(&path_explicit_mergeinfo,
&inherited,
svn_mergeinfo_explicit,
abspath_with_new_mergeinfo,
NULL, NULL, FALSE,
merge_b->ctx,
iterpool, iterpool));
if (path_explicit_mergeinfo)
{
SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url,
merge_b->ra_session2,
path_url,
iterpool));
SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(
&path_inherited_mergeinfo,
&inherited, NULL,
FALSE,
svn_mergeinfo_nearest_ancestor,
merge_b->ra_session2,
abspath_with_new_mergeinfo,
merge_b->ctx,
iterpool));
if (path_inherited_mergeinfo)
{
SVN_ERR(svn_mergeinfo_merge(path_explicit_mergeinfo,
path_inherited_mergeinfo,
iterpool));
SVN_ERR(svn_client__record_wc_mergeinfo(
abspath_with_new_mergeinfo,
path_explicit_mergeinfo,
FALSE, merge_b->ctx, iterpool));
}
new_child =
get_child_with_mergeinfo(notify_b->children_with_mergeinfo,
abspath_with_new_mergeinfo);
if (!new_child)
{
int parent_index =
find_nearest_ancestor(notify_b->children_with_mergeinfo,
FALSE, abspath_with_new_mergeinfo);
svn_client__merge_path_t *parent =
APR_ARRAY_IDX(notify_b->children_with_mergeinfo,
parent_index,
svn_client__merge_path_t *);
new_child = apr_pcalloc(pool, sizeof(*new_child));
new_child->abspath = apr_pstrdup(pool,
abspath_with_new_mergeinfo);
SVN_ERR_ASSERT(parent);
SVN_ERR_ASSERT(parent->remaining_ranges);
new_child->remaining_ranges = svn_rangelist_dup(
parent->remaining_ranges, pool);
insert_child_to_merge(notify_b->children_with_mergeinfo,
new_child, pool);
}
}
if (old_session_url)
SVN_ERR(svn_ra_reparent(merge_b->ra_session2, old_session_url,
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_boolean_t
path_is_subtree(const char *local_abspath,
apr_hash_t *subtrees,
apr_pool_t *pool)
{
if (local_abspath && subtrees)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(pool, subtrees);
hi;
hi = apr_hash_next(hi))
{
const char *path_touched_by_merge = svn__apr_hash_index_key(hi);
if (svn_dirent_is_ancestor(local_abspath, path_touched_by_merge))
return TRUE;
}
}
return FALSE;
}
static svn_boolean_t
subtree_touched_by_merge(const char *local_abspath,
notification_receiver_baton_t *notify_b,
apr_pool_t *pool)
{
return (path_is_subtree(local_abspath, notify_b->merged_abspaths, pool)
|| path_is_subtree(local_abspath, notify_b->skipped_abspaths, pool)
|| path_is_subtree(local_abspath, notify_b->added_abspaths, pool)
|| path_is_subtree(local_abspath,
notify_b->tree_conflicted_abspaths,
pool));
}
static svn_error_t *
do_mergeinfo_unaware_dir_merge(const char *url1,
svn_revnum_t revision1,
const char *url2,
svn_revnum_t revision2,
const char *target_dir_wcpath,
svn_depth_t depth,
notification_receiver_baton_t *notify_b,
merge_cmd_baton_t *merge_b,
apr_pool_t *pool)
{
svn_client__merge_path_t *item = apr_pcalloc(pool, sizeof(*item));
item->abspath = apr_pstrdup(pool, target_dir_wcpath);
item->remaining_ranges = svn_rangelist__initialize(revision1, revision2,
TRUE, pool);
APR_ARRAY_PUSH(notify_b->children_with_mergeinfo,
svn_client__merge_path_t *) = item;
return drive_merge_report_editor(target_dir_wcpath,
url1, revision1, url2, revision2,
NULL, depth, notify_b,
merge_b, pool);
}
static svn_error_t *
log_find_operative_subtree_revs(void *baton,
svn_log_entry_t *log_entry,
apr_pool_t *pool)
{
apr_hash_t *immediate_children = baton;
apr_hash_index_t *hi, *hi2;
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);
for (hi2 = apr_hash_first(pool, immediate_children);
hi2;
hi2 = apr_hash_next(hi2))
{
const char *immediate_path = svn__apr_hash_index_val(hi2);
if (svn_dirent_is_ancestor(immediate_path, path))
{
apr_hash_set(immediate_children, svn__apr_hash_index_key(hi2),
APR_HASH_KEY_STRING, NULL);
break;
}
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
get_inoperative_immediate_children(apr_hash_t **immediate_children,
apr_array_header_t *children_with_mergeinfo,
const char *merge_source_repos_abspath,
svn_revnum_t oldest_rev,
svn_revnum_t youngest_rev,
const char *merge_target_abspath,
svn_ra_session_t *ra_session,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
apr_pool_t *iterpool;
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
SVN_ERR_ASSERT(oldest_rev <= youngest_rev);
*immediate_children = apr_hash_make(result_pool);
iterpool = svn_pool_create(scratch_pool);
for (i = 0; i < children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
svn_pool_clear(iterpool);
if (child->immediate_child_dir)
apr_hash_set(*immediate_children,
apr_pstrdup(result_pool, child->abspath),
APR_HASH_KEY_STRING,
svn_fspath__join(merge_source_repos_abspath,
svn_dirent_is_child(merge_target_abspath,
child->abspath,
iterpool),
result_pool));
}
svn_pool_destroy(iterpool);
if (apr_hash_count(*immediate_children))
{
apr_array_header_t *log_targets = apr_array_make(scratch_pool, 1,
sizeof(const char *));
APR_ARRAY_PUSH(log_targets, const char *) = "";
SVN_ERR(svn_ra_get_log2(ra_session, log_targets, youngest_rev,
oldest_rev, 0, TRUE, FALSE, FALSE,
NULL, log_find_operative_subtree_revs,
*immediate_children, scratch_pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
record_mergeinfo_for_dir_merge(svn_mergeinfo_catalog_t result_catalog,
const svn_merge_range_t *merged_range,
const char *mergeinfo_path,
svn_depth_t depth,
svn_boolean_t squelch_mergeinfo_notifications,
notification_receiver_baton_t *notify_b,
merge_cmd_baton_t *merge_b,
apr_pool_t *pool)
{
int i;
svn_boolean_t is_rollback = (merged_range->start > merged_range->end);
svn_boolean_t operative_merge = FALSE;
apr_hash_t *inoperative_immediate_children = NULL;
apr_pool_t *iterpool = svn_pool_create(pool);
svn_merge_range_t range;
range.start = merged_range->start;
range.end = merged_range->end;
range.inheritable = merged_range->inheritable;
if ((notify_b->merged_abspaths
&& apr_hash_count(notify_b->merged_abspaths))
|| (notify_b->skipped_abspaths
&& apr_hash_count(notify_b->skipped_abspaths))
|| (notify_b->added_abspaths
&& apr_hash_count(notify_b->added_abspaths))
|| (notify_b->tree_conflicted_abspaths
&& apr_hash_count(notify_b->tree_conflicted_abspaths)))
operative_merge = TRUE;
if (!operative_merge)
range.inheritable = TRUE;
remove_absent_children(merge_b->target_abspath,
notify_b->children_with_mergeinfo, notify_b);
if (!merge_b->record_only && range.start <= range.end
&& depth == svn_depth_immediates)
SVN_ERR(get_inoperative_immediate_children(
&inoperative_immediate_children,
notify_b->children_with_mergeinfo,
mergeinfo_path, range.start + 1, range.end,
merge_b->target_abspath, merge_b->ra_session1,
pool, iterpool));
for (i = 0; i < notify_b->children_with_mergeinfo->nelts; i++)
{
const char *child_repos_path;
const char *child_merge_src_canon_path;
apr_array_header_t *child_merge_rangelist;
apr_hash_t *child_merges;
svn_client__merge_path_t *child =
APR_ARRAY_IDX(notify_b->children_with_mergeinfo, i,
svn_client__merge_path_t *);
SVN_ERR_ASSERT(child);
if (child->absent)
continue;
svn_pool_clear(iterpool);
if (i > 0
&& (!merge_b->record_only || merge_b->reintegrate_merge)
&& (!child->immediate_child_dir || child->pre_merge_mergeinfo)
&& (!operative_merge
|| !subtree_touched_by_merge(child->abspath, notify_b,
iterpool)))
{
if (child->child_of_noninheritable)
{
SVN_ERR(svn_client__record_wc_mergeinfo(child->abspath,
NULL, FALSE,
merge_b->ctx,
iterpool));
}
}
else
{
svn_boolean_t child_is_deleted;
svn_boolean_t rangelist_inheritance;
SVN_ERR(svn_wc__node_is_status_deleted(&child_is_deleted,
merge_b->ctx->wc_ctx,
child->abspath, iterpool));
if (child_is_deleted)
continue;
if (inoperative_immediate_children
&& apr_hash_get(inoperative_immediate_children,
child->abspath,
APR_HASH_KEY_STRING))
continue;
child_repos_path = svn_dirent_is_child(merge_b->target_abspath,
child->abspath, iterpool);
if (!child_repos_path)
child_repos_path = "";
child_merge_src_canon_path = svn_fspath__join(mergeinfo_path,
child_repos_path,
iterpool);
SVN_ERR(filter_natural_history_from_mergeinfo(
&child_merge_rangelist, child_merge_src_canon_path,
child->implicit_mergeinfo, &range, iterpool));
if (child_merge_rangelist->nelts == 0)
continue;
if (!squelch_mergeinfo_notifications)
{
svn_wc_notify_t *notify = svn_wc_create_notify(
child->abspath,
svn_wc_notify_merge_record_info_begin,
iterpool);
notify->merge_range = svn_merge_range_dup(merged_range,
iterpool);
merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
iterpool);
}
if (i == 0)
SVN_ERR(record_skips(mergeinfo_path,
child_merge_rangelist,
is_rollback, notify_b, merge_b, iterpool));
else if (notify_b->skipped_abspaths
&& apr_hash_get(notify_b->skipped_abspaths, child->abspath,
APR_HASH_KEY_STRING))
continue;
SVN_ERR(calculate_merge_inheritance(child_merge_rangelist,
&rangelist_inheritance,
child->abspath,
i == 0,
child->missing_child,
depth,
merge_b->ctx->wc_ctx,
iterpool));
if (child->inherited_mergeinfo)
SVN_ERR(svn_client__record_wc_mergeinfo(
child->abspath,
child->pre_merge_mergeinfo,
FALSE, merge_b->ctx,
iterpool));
if (merge_b->implicit_src_gap)
{
if (is_rollback)
SVN_ERR(svn_rangelist_reverse(child_merge_rangelist,
iterpool));
SVN_ERR(svn_rangelist_remove(&child_merge_rangelist,
merge_b->implicit_src_gap,
child_merge_rangelist, FALSE,
iterpool));
if (is_rollback)
SVN_ERR(svn_rangelist_reverse(child_merge_rangelist,
iterpool));
}
child_merges = apr_hash_make(iterpool);
if ((!merge_b->record_only || merge_b->reintegrate_merge)
&& (!is_rollback))
{
svn_error_t *err;
svn_opt_revision_t peg_revision;
svn_mergeinfo_t subtree_history_as_mergeinfo;
apr_array_header_t *child_merge_src_rangelist;
const char *old_session_url;
const char *subtree_mergeinfo_url =
svn_path_url_add_component2(merge_b->repos_root_url,
child_merge_src_canon_path + 1,
iterpool);
peg_revision.kind = svn_opt_revision_number;
peg_revision.value.number = merged_range->end;
SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url,
merge_b->ra_session2,
subtree_mergeinfo_url,
iterpool));
err = svn_client__get_history_as_mergeinfo(
&subtree_history_as_mergeinfo, NULL,
subtree_mergeinfo_url, &peg_revision,
MAX(merged_range->start, merged_range->end),
MIN(merged_range->start, merged_range->end),
merge_b->ra_session2, merge_b->ctx, iterpool);
if (err)
{
if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
return svn_error_trace(err);
svn_error_clear(err);
}
else
{
child_merge_src_rangelist = apr_hash_get(
subtree_history_as_mergeinfo,
child_merge_src_canon_path,
APR_HASH_KEY_STRING);
SVN_ERR(svn_rangelist_intersect(&child_merge_rangelist,
child_merge_rangelist,
child_merge_src_rangelist,
FALSE, iterpool));
if (!rangelist_inheritance)
svn_rangelist__set_inheritance(child_merge_rangelist,
FALSE);
}
if (old_session_url)
SVN_ERR(svn_ra_reparent(merge_b->ra_session2,
old_session_url, iterpool));
}
apr_hash_set(child_merges, child->abspath, APR_HASH_KEY_STRING,
child_merge_rangelist);
SVN_ERR(update_wc_mergeinfo(result_catalog,
child->abspath,
child_merge_src_canon_path,
child_merges, is_rollback,
merge_b->ctx, iterpool));
}
if (i > 0)
{
svn_boolean_t in_switched_subtree = FALSE;
if (child->switched)
in_switched_subtree = TRUE;
else if (i > 1)
{
svn_client__merge_path_t *parent;
int j = i - 1;
for (; j > 0; j--)
{
parent = APR_ARRAY_IDX(notify_b->children_with_mergeinfo,
j, svn_client__merge_path_t *);
if (parent
&& parent->switched
&& svn_dirent_is_ancestor(parent->abspath,
child->abspath))
{
in_switched_subtree = TRUE;
break;
}
}
}
SVN_ERR(svn_client__elide_mergeinfo(
child->abspath,
in_switched_subtree ? NULL : merge_b->target_abspath,
merge_b->ctx, iterpool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
record_mergeinfo_for_added_subtrees(
svn_merge_range_t *merged_range,
const char *mergeinfo_path,
svn_depth_t depth,
svn_boolean_t squelch_mergeinfo_notifications,
notification_receiver_baton_t *notify_b,
merge_cmd_baton_t *merge_b,
apr_pool_t *pool)
{
apr_pool_t *iterpool;
apr_hash_index_t *hi;
if (!notify_b->added_abspaths)
return SVN_NO_ERROR;
SVN_ERR_ASSERT(merged_range->start < merged_range->end);
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, notify_b->added_abspaths); hi;
hi = apr_hash_next(hi))
{
const char *added_abspath = svn__apr_hash_index_key(hi);
const char *dir_abspath;
svn_mergeinfo_t parent_mergeinfo;
svn_mergeinfo_t added_path_mergeinfo;
svn_boolean_t inherited;
apr_pool_clear(iterpool);
dir_abspath = svn_dirent_dirname(added_abspath, iterpool);
SVN_ERR(svn_client__get_wc_mergeinfo(&added_path_mergeinfo, &inherited,
svn_mergeinfo_explicit,
added_abspath, NULL, NULL, FALSE,
merge_b->ctx, iterpool, iterpool));
if (!added_path_mergeinfo)
SVN_ERR(svn_client__get_wc_mergeinfo(&parent_mergeinfo, &inherited,
svn_mergeinfo_explicit,
dir_abspath, NULL, NULL, FALSE,
merge_b->ctx,
iterpool, iterpool));
if (added_path_mergeinfo
|| svn_mergeinfo__is_noninheritable(parent_mergeinfo, iterpool))
{
svn_client__merge_path_t *target_merge_path =
APR_ARRAY_IDX(notify_b->children_with_mergeinfo, 0,
svn_client__merge_path_t *);
svn_merge_range_t *rng;
svn_node_kind_t added_path_kind;
svn_mergeinfo_t merge_mergeinfo;
svn_mergeinfo_t adds_history_as_mergeinfo;
apr_array_header_t *rangelist;
const char *rel_added_path;
const char *added_path_mergeinfo_path;
const char *old_session_url;
const char *added_path_mergeinfo_url;
svn_opt_revision_t peg_revision;
SVN_ERR(svn_wc_read_kind(&added_path_kind, merge_b->ctx->wc_ctx,
added_abspath, FALSE, iterpool));
merge_mergeinfo = apr_hash_make(iterpool);
rangelist = apr_array_make(iterpool, 1, sizeof(svn_merge_range_t *));
rng = svn_merge_range_dup(merged_range, iterpool);
if (added_path_kind == svn_node_file)
rng->inheritable = TRUE;
else
rng->inheritable =
(!(depth == svn_depth_infinity
|| depth == svn_depth_immediates));
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = rng;
rel_added_path = svn_dirent_is_child(target_merge_path->abspath,
added_abspath, iterpool);
SVN_ERR_ASSERT(rel_added_path);
added_path_mergeinfo_path = svn_dirent_join(mergeinfo_path,
rel_added_path,
iterpool);
apr_hash_set(merge_mergeinfo, added_path_mergeinfo_path,
APR_HASH_KEY_STRING, rangelist);
added_path_mergeinfo_url =
svn_path_url_add_component2(merge_b->repos_root_url,
added_path_mergeinfo_path + 1,
iterpool);
peg_revision.kind = svn_opt_revision_number;
peg_revision.value.number = MAX(merged_range->start,
merged_range->end);
SVN_ERR(svn_client__ensure_ra_session_url(
&old_session_url, merge_b->ra_session2,
added_path_mergeinfo_url, iterpool));
SVN_ERR(svn_client__get_history_as_mergeinfo(
&adds_history_as_mergeinfo, NULL,
added_path_mergeinfo_url, &peg_revision,
MAX(merged_range->start, merged_range->end),
MIN(merged_range->start, merged_range->end),
merge_b->ra_session2, merge_b->ctx, iterpool));
if (old_session_url)
SVN_ERR(svn_ra_reparent(merge_b->ra_session2,
old_session_url, iterpool));
SVN_ERR(svn_mergeinfo_intersect2(&merge_mergeinfo,
merge_mergeinfo,
adds_history_as_mergeinfo,
FALSE, iterpool, iterpool));
if (added_path_mergeinfo)
SVN_ERR(svn_mergeinfo_merge(merge_mergeinfo, added_path_mergeinfo,
iterpool));
SVN_ERR(svn_client__record_wc_mergeinfo(
added_abspath, merge_mergeinfo,
!squelch_mergeinfo_notifications, merge_b->ctx, iterpool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
typedef struct log_noop_baton_t
{
merge_cmd_baton_t *merge_b;
apr_array_header_t *children_with_mergeinfo;
const char *target_repos_abs;
const char *source_repos_abs;
apr_array_header_t *operative_ranges;
apr_array_header_t *merged_ranges;
apr_pool_t *pool;
} log_noop_baton_t;
static svn_error_t *
rangelist_merge_revision(apr_array_header_t *rangelist,
svn_revnum_t revision,
apr_pool_t *result_pool)
{
svn_merge_range_t *new_range;
if (rangelist->nelts)
{
svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
svn_merge_range_t *);
if (range->end == revision - 1)
{
range->end = revision;
return SVN_NO_ERROR;
}
}
new_range = apr_palloc(result_pool, sizeof(*new_range));
new_range->start = revision - 1;
new_range->end = revision;
new_range->inheritable = TRUE;
APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = new_range;
return SVN_NO_ERROR;
}
static svn_error_t *
log_noop_revs(void *baton,
svn_log_entry_t *log_entry,
apr_pool_t *scratch_pool)
{
log_noop_baton_t *log_gap_baton = baton;
apr_hash_index_t *hi;
svn_revnum_t revision;
svn_boolean_t log_entry_rev_required = FALSE;
revision = log_entry->revision;
SVN_ERR(rangelist_merge_revision(log_gap_baton->operative_ranges,
revision,
log_gap_baton->pool));
for (hi = apr_hash_first(scratch_pool, log_entry->changed_paths2);
hi;
hi = apr_hash_next(hi))
{
const char *path = svn__apr_hash_index_key(hi);
const char *rel_path;
const char *cwmi_path;
apr_array_header_t *paths_explicit_rangelist = NULL;
svn_boolean_t mergeinfo_inherited = FALSE;
rel_path = svn_fspath__skip_ancestor(log_gap_baton->source_repos_abs, path);
if (rel_path == NULL)
continue;
cwmi_path = svn_dirent_join(log_gap_baton->merge_b->target_abspath,
rel_path, scratch_pool);
while (!log_entry_rev_required)
{
svn_client__merge_path_t *child = get_child_with_mergeinfo(
log_gap_baton->children_with_mergeinfo, cwmi_path);
if (child && child->pre_merge_mergeinfo)
{
paths_explicit_rangelist =
apr_hash_get(child->pre_merge_mergeinfo, path,
APR_HASH_KEY_STRING);
break;
}
if (cwmi_path[0] == '\0'
|| svn_dirent_is_root(cwmi_path, strlen(cwmi_path))
|| svn_path_compare_paths(log_gap_baton->merge_b->target_abspath,
cwmi_path) == 0)
{
break;
}
cwmi_path = svn_dirent_dirname(cwmi_path, scratch_pool);
path = svn_dirent_dirname(path, scratch_pool);
mergeinfo_inherited = TRUE;
}
if (paths_explicit_rangelist)
{
apr_array_header_t *intersecting_range;
apr_array_header_t *rangelist;
rangelist = svn_rangelist__initialize(revision - 1, revision, TRUE,
scratch_pool);
SVN_ERR(svn_rangelist_intersect(&intersecting_range,
paths_explicit_rangelist,
rangelist,
mergeinfo_inherited, scratch_pool));
if (intersecting_range->nelts == 0)
log_entry_rev_required = TRUE;
}
else
{
log_entry_rev_required = TRUE;
}
}
if (!log_entry_rev_required)
SVN_ERR(rangelist_merge_revision(log_gap_baton->merged_ranges,
revision,
log_gap_baton->pool));
return SVN_NO_ERROR;
}
static svn_error_t *
remove_noop_subtree_ranges(const char *url1,
svn_revnum_t revision1,
const char *url2,
svn_revnum_t revision2,
svn_ra_session_t *ra_session,
notification_receiver_baton_t *notify_b,
merge_cmd_baton_t *merge_b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
int i;
svn_client__merge_path_t *root_child =
APR_ARRAY_IDX(notify_b->children_with_mergeinfo, 0,
svn_client__merge_path_t *);
apr_array_header_t *requested_ranges;
apr_array_header_t *subtree_gap_ranges;
apr_array_header_t *subtree_remaining_ranges;
apr_array_header_t *log_targets;
apr_array_header_t *merged_ranges;
apr_array_header_t *operative_ranges;
log_noop_baton_t log_gap_baton;
svn_merge_range_t *oldest_gap_rev;
svn_merge_range_t *youngest_gap_rev;
apr_array_header_t *inoperative_ranges;
const char *repos_root_url;
if (revision1 > revision2)
return SVN_NO_ERROR;
if (notify_b->children_with_mergeinfo->nelts < 2)
return SVN_NO_ERROR;
subtree_remaining_ranges = apr_array_make(scratch_pool, 1,
sizeof(svn_merge_range_t *));
log_targets = apr_array_make(scratch_pool, 1, sizeof(const char *));
merged_ranges = apr_array_make(scratch_pool, 0, sizeof(svn_revnum_t *));
operative_ranges = apr_array_make(scratch_pool, 0, sizeof(svn_revnum_t *));
requested_ranges = svn_rangelist__initialize(MIN(revision1, revision2),
MAX(revision1, revision2),
TRUE, scratch_pool);
SVN_ERR(svn_rangelist_remove(&subtree_gap_ranges,
root_child->remaining_ranges,
requested_ranges, FALSE, scratch_pool));
if (!subtree_gap_ranges->nelts)
return SVN_NO_ERROR;
for (i = 1; i < notify_b->children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(notify_b->children_with_mergeinfo, i,
svn_client__merge_path_t *);
if (child->remaining_ranges && child->remaining_ranges->nelts)
SVN_ERR(svn_rangelist_merge(&subtree_remaining_ranges,
child->remaining_ranges,
scratch_pool));
}
if (!subtree_remaining_ranges->nelts)
return SVN_NO_ERROR;
SVN_ERR(svn_rangelist_intersect(&subtree_gap_ranges,
subtree_gap_ranges,
subtree_remaining_ranges, FALSE,
scratch_pool));
if (!subtree_gap_ranges->nelts)
return SVN_NO_ERROR;
oldest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges, 0, svn_merge_range_t *);
youngest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges,
subtree_gap_ranges->nelts - 1, svn_merge_range_t *);
SVN_ERR(svn_wc__node_get_repos_info(&repos_root_url, NULL,
merge_b->ctx->wc_ctx,
merge_b->target_abspath,
scratch_pool, scratch_pool));
log_gap_baton.merge_b = merge_b;
log_gap_baton.children_with_mergeinfo = notify_b->children_with_mergeinfo;
SVN_ERR(svn_client__path_relative_to_root(
&(log_gap_baton.target_repos_abs), merge_b->ctx->wc_ctx,
merge_b->target_abspath, repos_root_url, TRUE, NULL,
result_pool, scratch_pool));
SVN_ERR(svn_client__path_relative_to_root(
&(log_gap_baton.source_repos_abs), merge_b->ctx->wc_ctx,
url2, repos_root_url, TRUE, NULL,
result_pool, scratch_pool));
log_gap_baton.merged_ranges = merged_ranges;
log_gap_baton.operative_ranges = operative_ranges;
log_gap_baton.pool = svn_pool_create(scratch_pool);
APR_ARRAY_PUSH(log_targets, const char *) = "";
SVN_ERR(svn_ra_get_log2(ra_session, log_targets, oldest_gap_rev->start + 1,
youngest_gap_rev->end, 0, TRUE, TRUE, FALSE,
apr_array_make(scratch_pool, 0,
sizeof(const char *)),
log_noop_revs, &log_gap_baton, scratch_pool));
inoperative_ranges = svn_rangelist__initialize(oldest_gap_rev->start,
youngest_gap_rev->end,
TRUE, scratch_pool);
SVN_ERR(svn_rangelist_remove(&(inoperative_ranges),
log_gap_baton.operative_ranges,
inoperative_ranges, FALSE, scratch_pool));
SVN_ERR(svn_rangelist_merge(&(log_gap_baton.merged_ranges),
inoperative_ranges, scratch_pool));
for (i = 1; i < notify_b->children_with_mergeinfo->nelts; i++)
{
svn_client__merge_path_t *child =
APR_ARRAY_IDX(notify_b->children_with_mergeinfo, i,
svn_client__merge_path_t *);
if (child->remaining_ranges && child->remaining_ranges->nelts)
{
SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
log_gap_baton.merged_ranges,
child->remaining_ranges,
FALSE, result_pool));
}
}
svn_pool_destroy(log_gap_baton.pool);
return SVN_NO_ERROR;
}
static svn_error_t *
do_directory_merge(svn_mergeinfo_catalog_t result_catalog,
const char *url1,
svn_revnum_t revision1,
const char *url2,
svn_revnum_t revision2,
const char *target_abspath,
svn_depth_t depth,
svn_boolean_t squelch_mergeinfo_notifications,
svn_boolean_t abort_on_conflicts,
notification_receiver_baton_t *notify_b,
merge_cmd_baton_t *merge_b,
apr_pool_t *scratch_pool)
{
svn_error_t *err = SVN_NO_ERROR;
svn_error_t *merge_conflict_err = SVN_NO_ERROR;
svn_merge_range_t range;
svn_ra_session_t *ra_session;
svn_client__merge_path_t *target_merge_path;
svn_boolean_t is_rollback = (revision1 > revision2);
const char *primary_url = is_rollback ? url1 : url2;
const char *source_root_url, *mergeinfo_path;
svn_boolean_t honor_mergeinfo, record_mergeinfo;
svn_boolean_t same_urls = (strcmp(url1, url2) == 0);
honor_mergeinfo = HONOR_MERGEINFO(merge_b);
record_mergeinfo = RECORD_MERGEINFO(merge_b);
notify_b->children_with_mergeinfo =
apr_array_make(scratch_pool, 0, sizeof(svn_client__merge_path_t *));
if (!honor_mergeinfo)
return do_mergeinfo_unaware_dir_merge(url1, revision1, url2, revision2,
target_abspath, depth,
notify_b, merge_b, scratch_pool);
ra_session = is_rollback ? merge_b->ra_session1 : merge_b->ra_session2;
SVN_ERR(svn_ra_get_repos_root2(ra_session, &source_root_url, scratch_pool));
SVN_ERR(svn_client__path_relative_to_root(&mergeinfo_path,
merge_b->ctx->wc_ctx, primary_url,
source_root_url, TRUE, NULL,
scratch_pool, scratch_pool));
SVN_ERR(get_mergeinfo_paths(notify_b->children_with_mergeinfo, merge_b,
mergeinfo_path, source_root_url,
url1, url2, revision1, revision2,
ra_session, depth, scratch_pool, scratch_pool));
target_merge_path = APR_ARRAY_IDX(notify_b->children_with_mergeinfo, 0,
svn_client__merge_path_t *);
merge_b->target_missing_child = target_merge_path->missing_child;
SVN_ERR(populate_remaining_ranges(notify_b->children_with_mergeinfo,
source_root_url,
url1, revision1, url2, revision2,
honor_mergeinfo,
ra_session, mergeinfo_path,
merge_b, scratch_pool, scratch_pool));
range.start = revision1;
range.end = revision2;
range.inheritable = TRUE;
if (honor_mergeinfo && !merge_b->reintegrate_merge)
{
svn_revnum_t new_range_start, start_rev;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
new_range_start = get_most_inclusive_start_rev(
notify_b->children_with_mergeinfo, is_rollback);
if (SVN_IS_VALID_REVNUM(new_range_start))
range.start = new_range_start;
if (!is_rollback)
SVN_ERR(remove_noop_subtree_ranges(url1, revision1, url2, revision2,
ra_session,
notify_b, merge_b,
scratch_pool, iterpool));
SVN_ERR(fix_deleted_subtree_ranges(url1, revision1, url2, revision2,
ra_session, notify_b, merge_b,
scratch_pool, iterpool));
start_rev =
get_most_inclusive_start_rev(notify_b->children_with_mergeinfo,
is_rollback);
if (SVN_IS_VALID_REVNUM(start_rev))
{
svn_revnum_t end_rev =
get_most_inclusive_end_rev(notify_b->children_with_mergeinfo,
is_rollback);
while (end_rev != SVN_INVALID_REVNUM)
{
svn_revnum_t next_end_rev;
const char *real_url1 = url1, *real_url2 = url2;
const char *old_sess1_url = NULL, *old_sess2_url = NULL;
svn_merge_range_t *first_target_range = APR_ARRAY_IDX(
target_merge_path->remaining_ranges, 0, svn_merge_range_t *);
if (first_target_range
&& start_rev != first_target_range->start)
{
if (is_rollback)
{
if (end_rev < first_target_range->start)
end_rev = first_target_range->start;
}
else
{
if (end_rev > first_target_range->start)
end_rev = first_target_range->start;
}
}
svn_pool_clear(iterpool);
slice_remaining_ranges(notify_b->children_with_mergeinfo,
is_rollback, end_rev, scratch_pool);
notify_b->cur_ancestor_index = -1;
if (! same_urls)
{
if (is_rollback && (end_rev != revision2))
{
real_url2 = url1;
SVN_ERR(svn_client__ensure_ra_session_url
(&old_sess2_url, merge_b->ra_session2,
real_url2, iterpool));
}
if ((! is_rollback) && (start_rev != revision1))
{
real_url1 = url2;
SVN_ERR(svn_client__ensure_ra_session_url
(&old_sess1_url, merge_b->ra_session1,
real_url1, iterpool));
}
}
SVN_ERR(drive_merge_report_editor(
merge_b->target_abspath,
real_url1, start_rev,
real_url2, end_rev,
notify_b->children_with_mergeinfo,
depth, notify_b,
merge_b,
iterpool));
if (old_sess1_url)
SVN_ERR(svn_ra_reparent(merge_b->ra_session1,
old_sess1_url, iterpool));
if (old_sess2_url)
SVN_ERR(svn_ra_reparent(merge_b->ra_session2,
old_sess2_url, iterpool));
SVN_ERR(process_children_with_new_mergeinfo(merge_b, notify_b,
scratch_pool));
remove_children_with_deleted_mergeinfo(merge_b, notify_b);
remove_first_range_from_remaining_ranges(
end_rev, notify_b->children_with_mergeinfo, scratch_pool);
next_end_rev =
get_most_inclusive_end_rev(notify_b->children_with_mergeinfo,
is_rollback);
if ((next_end_rev != SVN_INVALID_REVNUM || abort_on_conflicts)
&& is_path_conflicted_by_merge(merge_b))
{
svn_merge_range_t conflicted_range;
conflicted_range.start = start_rev;
conflicted_range.end = end_rev;
merge_conflict_err = make_merge_conflict_error(
merge_b->target_abspath,
&conflicted_range,
scratch_pool);
range.end = end_rev;
break;
}
start_rev =
get_most_inclusive_start_rev(notify_b->children_with_mergeinfo,
is_rollback);
end_rev = next_end_rev;
}
}
svn_pool_destroy(iterpool);
}
else
{
if (!merge_b->record_only)
{
notify_b->cur_ancestor_index = -1;
SVN_ERR(drive_merge_report_editor(merge_b->target_abspath,
url1, revision1, url2, revision2,
NULL,
depth, notify_b,
merge_b,
scratch_pool));
}
}
if (record_mergeinfo)
{
err = record_mergeinfo_for_dir_merge(result_catalog,
&range,
mergeinfo_path,
depth,
squelch_mergeinfo_notifications,
notify_b,
merge_b,
scratch_pool);
if (err == SVN_NO_ERROR
&& (range.start < range.end))
{
err = record_mergeinfo_for_added_subtrees(
&range, mergeinfo_path, depth,
squelch_mergeinfo_notifications,
notify_b, merge_b, scratch_pool);
}
}
return svn_error_compose_create(err, merge_conflict_err);
}
static svn_error_t *
ensure_ra_session_url(svn_ra_session_t **ra_session,
const char *url,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_error_t *err = SVN_NO_ERROR;
if (*ra_session)
{
const char *old_session_url;
err = svn_client__ensure_ra_session_url(&old_session_url,
*ra_session,
url,
pool);
}
if (! *ra_session || (err && err->apr_err == SVN_ERR_RA_ILLEGAL_URL))
{
svn_error_clear(err);
err = svn_client__open_ra_session_internal(ra_session, NULL, url, NULL,
NULL, FALSE, TRUE, ctx, pool);
}
SVN_ERR(err);
return SVN_NO_ERROR;
}
static svn_error_t *
do_merge(apr_hash_t **modified_subtrees,
svn_mergeinfo_catalog_t result_catalog,
const apr_array_header_t *merge_sources,
const char *target_abspath,
svn_boolean_t sources_ancestral,
svn_boolean_t sources_related,
svn_boolean_t same_repos,
svn_boolean_t ignore_ancestry,
svn_boolean_t force,
svn_boolean_t dry_run,
svn_boolean_t record_only,
apr_hash_t *record_only_paths,
svn_boolean_t reintegrate_merge,
svn_boolean_t squelch_mergeinfo_notifications,
svn_depth_t depth,
const apr_array_header_t *merge_options,
svn_boolean_t *use_sleep,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t merge_cmd_baton;
notification_receiver_baton_t notify_baton;
svn_config_t *cfg;
const char *diff3_cmd;
int i;
svn_boolean_t checked_mergeinfo_capability = FALSE;
svn_ra_session_t *ra_session1 = NULL, *ra_session2 = NULL;
svn_node_kind_t target_kind;
apr_pool_t *iterpool;
SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
if (record_only)
{
if (! sources_ancestral)
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Use of two URLs is not compatible with "
"mergeinfo modification"));
if (! same_repos)
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Merge from foreign repository is not "
"compatible with mergeinfo modification"));
if (dry_run)
return SVN_NO_ERROR;
}
iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_wc_read_kind(&target_kind, ctx->wc_ctx, target_abspath, FALSE,
iterpool));
if (target_kind != svn_node_dir && target_kind != svn_node_file)
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("Merge target '%s' does not exist in the "
"working copy"), target_abspath);
if (depth == svn_depth_unknown)
depth = svn_depth_infinity;
cfg = ctx->config ? apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
APR_HASH_KEY_STRING) : NULL;
svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_DIFF3_CMD, NULL);
if (diff3_cmd != NULL)
SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool));
merge_cmd_baton.force = force;
merge_cmd_baton.dry_run = dry_run;
merge_cmd_baton.record_only = record_only;
merge_cmd_baton.ignore_ancestry = ignore_ancestry;
merge_cmd_baton.same_repos = same_repos;
merge_cmd_baton.mergeinfo_capable = FALSE;
merge_cmd_baton.sources_ancestral = sources_ancestral;
merge_cmd_baton.ctx = ctx;
merge_cmd_baton.target_missing_child = FALSE;
merge_cmd_baton.reintegrate_merge = reintegrate_merge;
merge_cmd_baton.target_abspath = target_abspath;
SVN_ERR(svn_wc__node_get_repos_info(&merge_cmd_baton.repos_root_url, NULL,
ctx->wc_ctx,
merge_cmd_baton.target_abspath,
result_pool,
iterpool));
merge_cmd_baton.pool = iterpool;
merge_cmd_baton.merge_options = merge_options;
merge_cmd_baton.diff3_cmd = diff3_cmd;
merge_cmd_baton.use_sleep = use_sleep;
notify_baton.wrapped_func = ctx->notify_func2;
notify_baton.wrapped_baton = ctx->notify_baton2;
notify_baton.nbr_notifications = 0;
notify_baton.nbr_operative_notifications = 0;
if (record_only && record_only_paths)
notify_baton.merged_abspaths = record_only_paths;
else
notify_baton.merged_abspaths = NULL;
notify_baton.skipped_abspaths = NULL;
notify_baton.added_abspaths = NULL;
notify_baton.tree_conflicted_abspaths = NULL;
notify_baton.is_single_file_merge = FALSE;
notify_baton.children_with_mergeinfo = NULL;
notify_baton.cur_ancestor_index = -1;
notify_baton.merge_b = &merge_cmd_baton;
notify_baton.pool = result_pool;
for (i = 0; i < merge_sources->nelts; i++)
{
merge_source_t *merge_source =
APR_ARRAY_IDX(merge_sources, i, merge_source_t *);
const char *url1, *url2;
svn_revnum_t rev1, rev2;
svn_boolean_t abort_on_conflicts = (i < merge_sources->nelts - 1);
svn_pool_clear(iterpool);
url1 = merge_source->url1;
url2 = merge_source->url2;
rev1 = merge_source->rev1;
rev2 = merge_source->rev2;
if ((strcmp(url1, url2) == 0) && (rev1 == rev2))
continue;
SVN_ERR(ensure_ra_session_url(&ra_session1, url1, ctx, scratch_pool));
SVN_ERR(ensure_ra_session_url(&ra_session2, url2, ctx, scratch_pool));
merge_cmd_baton.merge_source = *merge_source;
merge_cmd_baton.implicit_src_gap = NULL;
merge_cmd_baton.added_path = NULL;
merge_cmd_baton.add_necessitated_merge = FALSE;
merge_cmd_baton.dry_run_deletions =
dry_run ? apr_hash_make(iterpool) : NULL;
merge_cmd_baton.dry_run_added =
dry_run ? apr_hash_make(iterpool) : NULL;
merge_cmd_baton.conflicted_paths = NULL;
merge_cmd_baton.paths_with_new_mergeinfo = NULL;
merge_cmd_baton.paths_with_deleted_mergeinfo = NULL;
merge_cmd_baton.ra_session1 = ra_session1;
merge_cmd_baton.ra_session2 = ra_session2;
if (! checked_mergeinfo_capability)
{
SVN_ERR(svn_ra_has_capability(ra_session1,
&merge_cmd_baton.mergeinfo_capable,
SVN_RA_CAPABILITY_MERGEINFO,
iterpool));
checked_mergeinfo_capability = TRUE;
}
if (target_kind == svn_node_file)
{
SVN_ERR(do_file_merge(result_catalog,
url1, rev1, url2, rev2, target_abspath,
sources_related,
squelch_mergeinfo_notifications,
¬ify_baton,
abort_on_conflicts,
&merge_cmd_baton, iterpool));
}
else if (target_kind == svn_node_dir)
{
SVN_ERR(do_directory_merge(result_catalog,
url1, rev1, url2, rev2, target_abspath,
depth, squelch_mergeinfo_notifications,
abort_on_conflicts,
¬ify_baton, &merge_cmd_baton,
iterpool));
if (modified_subtrees)
{
if (notify_baton.merged_abspaths)
*modified_subtrees =
apr_hash_overlay(result_pool, *modified_subtrees,
notify_baton.merged_abspaths);
if (notify_baton.added_abspaths)
*modified_subtrees =
apr_hash_overlay(result_pool, *modified_subtrees,
notify_baton.added_abspaths);
if (notify_baton.skipped_abspaths)
*modified_subtrees =
apr_hash_overlay(result_pool, *modified_subtrees,
notify_baton.skipped_abspaths);
if (notify_baton.tree_conflicted_abspaths)
*modified_subtrees =
apr_hash_overlay(result_pool, *modified_subtrees,
notify_baton.tree_conflicted_abspaths);
}
}
if (! dry_run)
SVN_ERR(svn_client__elide_mergeinfo(target_abspath, NULL,
ctx, iterpool));
}
if (ctx->notify_func2)
{
svn_wc_notify_t *notify
= svn_wc_create_notify(target_abspath, svn_wc_notify_merge_completed,
iterpool);
notify->kind = svn_node_none;
notify->content_state = notify->prop_state
= svn_wc_notify_state_inapplicable;
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
notify->revision = SVN_INVALID_REVNUM;
(*ctx->notify_func2)(ctx->notify_baton2, notify, iterpool);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
merge_cousins_and_supplement_mergeinfo(const char *target_abspath,
svn_ra_session_t *URL1_ra_session,
svn_ra_session_t *URL2_ra_session,
const char *URL1,
svn_revnum_t rev1,
const char *URL2,
svn_revnum_t rev2,
svn_revnum_t yc_rev,
const char *source_repos_root,
const char *wc_repos_root,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
svn_boolean_t force,
svn_boolean_t record_only,
svn_boolean_t dry_run,
const apr_array_header_t *merge_options,
svn_boolean_t *use_sleep,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
svn_opt_revision_range_t *range;
apr_array_header_t *remove_sources, *add_sources, *ranges;
svn_opt_revision_t peg_revision;
svn_boolean_t same_repos;
apr_hash_t *modified_subtrees = NULL;
apr_pool_t *subpool = svn_pool_create(scratch_pool);
SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
if (strcmp(wc_repos_root, source_repos_root) != 0)
{
const char *source_repos_uuid;
const char *wc_repos_uuid;
SVN_ERR(svn_ra_get_uuid2(URL1_ra_session, &source_repos_uuid,
subpool));
SVN_ERR(svn_client_uuid_from_path2(&wc_repos_uuid, target_abspath,
ctx, subpool, subpool));
same_repos = (strcmp(wc_repos_uuid, source_repos_uuid) == 0);
}
else
same_repos = TRUE;
peg_revision.kind = svn_opt_revision_number;
range = apr_pcalloc(scratch_pool, sizeof(*range));
range->start.kind = svn_opt_revision_number;
range->start.value.number = rev1;
range->end.kind = svn_opt_revision_number;
range->end.value.number = yc_rev;
ranges = apr_array_make(scratch_pool, 2,
sizeof(svn_opt_revision_range_t *));
APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
peg_revision.value.number = rev1;
SVN_ERR(normalize_merge_sources(&remove_sources, URL1, URL1,
source_repos_root, &peg_revision,
ranges, URL1_ra_session, ctx, scratch_pool,
subpool));
range = apr_pcalloc(scratch_pool, sizeof(*range));
range->start.kind = svn_opt_revision_number;
range->start.value.number = yc_rev;
range->end.kind = svn_opt_revision_number;
range->end.value.number = rev2;
ranges = apr_array_make(scratch_pool, 2,
sizeof(svn_opt_revision_range_t *));
APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
peg_revision.value.number = rev2;
SVN_ERR(normalize_merge_sources(&add_sources, URL2, URL2,
source_repos_root, &peg_revision,
ranges, URL2_ra_session, ctx,
scratch_pool, subpool));
if (! record_only)
{
merge_source_t *faux_source;
apr_array_header_t *faux_sources =
apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
modified_subtrees = apr_hash_make(scratch_pool);
faux_source = apr_pcalloc(scratch_pool, sizeof(*faux_source));
faux_source->url1 = URL1;
faux_source->url2 = URL2;
faux_source->rev1 = rev1;
faux_source->rev2 = rev2;
APR_ARRAY_PUSH(faux_sources, merge_source_t *) = faux_source;
SVN_ERR(do_merge(&modified_subtrees, NULL, faux_sources, target_abspath,
FALSE, TRUE, same_repos,
ignore_ancestry, force, dry_run, FALSE, NULL, TRUE,
FALSE, depth, merge_options, use_sleep, ctx,
scratch_pool, subpool));
}
else if (! same_repos)
{
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
_("Merge from foreign repository is not "
"compatible with mergeinfo modification"));
}
if (same_repos && !dry_run)
{
svn_wc_notify_t *notify = svn_wc_create_notify(
target_abspath, svn_wc_notify_merge_record_info_begin, scratch_pool);
svn_mergeinfo_catalog_t add_result_catalog =
apr_hash_make(scratch_pool);
svn_mergeinfo_catalog_t remove_result_catalog =
apr_hash_make(scratch_pool);
ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
svn_pool_clear(subpool);
SVN_ERR(do_merge(NULL, add_result_catalog, add_sources, target_abspath,
TRUE, TRUE, same_repos,
ignore_ancestry, force, dry_run, TRUE,
modified_subtrees, TRUE,
TRUE, depth, merge_options, use_sleep, ctx,
scratch_pool, subpool));
svn_pool_clear(subpool);
SVN_ERR(do_merge(NULL, remove_result_catalog, remove_sources,
target_abspath, TRUE, TRUE, same_repos,
ignore_ancestry, force, dry_run, TRUE,
modified_subtrees, TRUE,
TRUE, depth, merge_options, use_sleep, ctx,
scratch_pool, subpool));
SVN_ERR(svn_mergeinfo_catalog_merge(add_result_catalog,
remove_result_catalog,
scratch_pool, scratch_pool));
if (apr_hash_count(add_result_catalog))
{
int i;
apr_array_header_t *sorted_cat =
svn_sort__hash(add_result_catalog,
svn_sort_compare_items_as_paths,
scratch_pool);
for (i = 0; i < sorted_cat->nelts; i++)
{
svn_sort__item_t elt = APR_ARRAY_IDX(sorted_cat, i,
svn_sort__item_t);
svn_error_t *err;
svn_pool_clear(subpool);
err = svn_client__record_wc_mergeinfo(elt.key,
elt.value,
TRUE, ctx,
subpool);
if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
{
svn_error_clear(err);
}
else
{
SVN_ERR(err);
}
}
}
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
static svn_error_t *
ensure_wc_is_suitable_merge_target(const char *target_abspath,
svn_client_ctx_t *ctx,
svn_boolean_t allow_mixed_rev,
svn_boolean_t allow_local_mods,
svn_boolean_t allow_switched_subtrees,
apr_pool_t *scratch_pool)
{
if (allow_mixed_rev && allow_local_mods && allow_switched_subtrees)
return SVN_NO_ERROR;
if (! allow_mixed_rev)
{
svn_revnum_t min_rev;
svn_revnum_t max_rev;
SVN_ERR(svn_client_min_max_revisions(&min_rev, &max_rev, target_abspath,
FALSE, ctx, scratch_pool));
if (!(SVN_IS_VALID_REVNUM(min_rev) && SVN_IS_VALID_REVNUM(max_rev)))
{
svn_boolean_t is_added;
SVN_ERR(svn_wc__node_is_added(&is_added, ctx->wc_ctx, target_abspath,
scratch_pool));
if (is_added)
return SVN_NO_ERROR;
else
return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("Cannot determine revision of working "
"copy"));
}
if (min_rev != max_rev)
return svn_error_createf(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
_("Cannot merge into mixed-revision working "
"copy [%lu:%lu]; try updating first"),
min_rev, max_rev);
}
if (! allow_switched_subtrees)
{
svn_boolean_t is_switched;
SVN_ERR(svn_wc__has_switched_subtrees(&is_switched, ctx->wc_ctx,
target_abspath, NULL,
scratch_pool));
if (is_switched)
return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("Cannot merge into a working copy "
"with a switched subtree"));
}
if (! allow_local_mods)
{
svn_boolean_t is_modified;
SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx,
target_abspath,
ctx->cancel_func,
ctx->cancel_baton,
scratch_pool));
if (is_modified)
return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("Cannot merge into a working copy "
"that has local modifications"));
}
return SVN_NO_ERROR;
}
static svn_error_t *
merge_locked(const char *source1,
const svn_opt_revision_t *revision1,
const char *source2,
const svn_opt_revision_t *revision2,
const char *target_abspath,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
svn_boolean_t force,
svn_boolean_t record_only,
svn_boolean_t dry_run,
svn_boolean_t allow_mixed_rev,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
const char *URL1, *URL2;
svn_revnum_t rev1, rev2;
svn_boolean_t related = FALSE, ancestral = FALSE;
const char *wc_repos_root, *source_repos_root;
svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
svn_ra_session_t *ra_session1, *ra_session2;
apr_array_header_t *merge_sources;
merge_source_t *merge_source;
svn_error_t *err;
svn_boolean_t use_sleep = FALSE;
const char *yc_path = NULL;
svn_revnum_t yc_rev = SVN_INVALID_REVNUM;
apr_pool_t *sesspool;
svn_boolean_t same_repos;
const char *source_repos_uuid1, *source_repos_uuid2;
svn_node_kind_t target_kind;
SVN_ERR(svn_io_check_path(target_abspath, &target_kind, scratch_pool));
if (target_kind == svn_node_none)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("Path '%s' does not exist"),
svn_dirent_local_style(target_abspath,
scratch_pool));
if ((revision1->kind == svn_opt_revision_unspecified)
|| (revision2->kind == svn_opt_revision_unspecified))
return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
_("Not all required revisions are specified"));
if (svn_path_is_url(source1) != svn_path_is_url(source2))
return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
_("Merge sources must both be "
"either paths or URLs"));
SVN_ERR(svn_client_url_from_path2(&URL1, source1, ctx,
scratch_pool, scratch_pool));
if (! URL1)
return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("'%s' has no URL"),
svn_dirent_local_style(source1, scratch_pool));
SVN_ERR(svn_client_url_from_path2(&URL2, source2, ctx,
scratch_pool, scratch_pool));
if (! URL2)
return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("'%s' has no URL"),
svn_dirent_local_style(source2, scratch_pool));
SVN_ERR(svn_wc_read_kind(&target_kind, ctx->wc_ctx, target_abspath, FALSE,
scratch_pool));
if (target_kind != svn_node_dir && target_kind != svn_node_file)
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("Merge target '%s' does not exist in the "
"working copy"), target_abspath);
SVN_ERR(ensure_wc_is_suitable_merge_target(target_abspath, ctx,
allow_mixed_rev, TRUE, TRUE,
scratch_pool));
SVN_ERR(svn_client__get_repos_root(&wc_repos_root, target_abspath,
ctx, scratch_pool, scratch_pool));
sesspool = svn_pool_create(scratch_pool);
SVN_ERR(svn_client__open_ra_session_internal(&ra_session1, NULL, URL1,
NULL, NULL, FALSE, TRUE,
ctx, sesspool));
SVN_ERR(svn_client__open_ra_session_internal(&ra_session2, NULL, URL2,
NULL, NULL, FALSE, TRUE,
ctx, sesspool));
SVN_ERR(svn_client__get_revision_number(&rev1, &youngest_rev, ctx->wc_ctx,
NULL, ra_session1, revision1,
sesspool));
SVN_ERR(svn_client__get_revision_number(&rev2, &youngest_rev, ctx->wc_ctx,
NULL, ra_session2, revision2,
sesspool));
SVN_ERR(svn_ra_get_uuid2(ra_session1, &source_repos_uuid1, scratch_pool));
SVN_ERR(svn_ra_get_uuid2(ra_session2, &source_repos_uuid2, scratch_pool));
if (strcmp(source_repos_uuid1, source_repos_uuid2) != 0)
return svn_error_createf(SVN_ERR_RA_UUID_MISMATCH, NULL,
_("'%s' isn't in the same repository as '%s'"),
URL1, URL2);
SVN_ERR(svn_ra_get_repos_root2(ra_session1, &source_repos_root, sesspool));
if (strcmp(wc_repos_root, source_repos_root) != 0)
{
const char *wc_repos_uuid;
SVN_ERR(svn_client_uuid_from_path2(&wc_repos_uuid, target_abspath,
ctx, scratch_pool, scratch_pool));
same_repos = (strcmp(wc_repos_uuid, source_repos_uuid1) == 0);
}
else
same_repos = TRUE;
if (! ignore_ancestry)
SVN_ERR(svn_client__get_youngest_common_ancestor(&yc_path, &yc_rev,
URL1, rev1,
URL2, rev2,
ctx, scratch_pool));
if (yc_path && SVN_IS_VALID_REVNUM(yc_rev))
{
apr_array_header_t *ranges;
svn_opt_revision_range_t *range;
svn_opt_revision_t peg_revision;
peg_revision.kind = svn_opt_revision_number;
related = TRUE;
yc_path = svn_path_url_add_component2(source_repos_root, yc_path,
scratch_pool);
if ((strcmp(yc_path, URL2) == 0) && (yc_rev == rev2))
{
ancestral = TRUE;
range = apr_pcalloc(scratch_pool, sizeof(*range));
range->start.kind = svn_opt_revision_number;
range->start.value.number = rev1;
range->end.kind = svn_opt_revision_number;
range->end.value.number = yc_rev;
ranges = apr_array_make(scratch_pool,
2, sizeof(svn_opt_revision_range_t *));
APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
peg_revision.value.number = rev1;
SVN_ERR(normalize_merge_sources(&merge_sources, URL1, URL1,
source_repos_root, &peg_revision,
ranges, ra_session1, ctx,
scratch_pool, scratch_pool));
}
else if ((strcmp(yc_path, URL1) == 0) && (yc_rev == rev1))
{
ancestral = TRUE;
range = apr_pcalloc(scratch_pool, sizeof(*range));
range->start.kind = svn_opt_revision_number;
range->start.value.number = yc_rev;
range->end.kind = svn_opt_revision_number;
range->end.value.number = rev2;
ranges = apr_array_make(scratch_pool,
2, sizeof(svn_opt_revision_range_t *));
APR_ARRAY_PUSH(ranges, svn_opt_revision_range_t *) = range;
peg_revision.value.number = rev2;
SVN_ERR(normalize_merge_sources(&merge_sources, URL2, URL2,
source_repos_root, &peg_revision,
ranges, ra_session2, ctx,
scratch_pool, scratch_pool));
}
else
{
err = merge_cousins_and_supplement_mergeinfo(target_abspath,
ra_session1,
ra_session2,
URL1, rev1,
URL2, rev2,
yc_rev,
source_repos_root,
wc_repos_root,
depth,
ignore_ancestry, force,
record_only, dry_run,
merge_options,
&use_sleep, ctx,
scratch_pool);
if (err)
{
if (use_sleep)
svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
return svn_error_trace(err);
}
svn_pool_destroy(sesspool);
return SVN_NO_ERROR;
}
}
else
{
merge_sources = apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
merge_source = apr_pcalloc(scratch_pool, sizeof(*merge_source));
merge_source->url1 = URL1;
merge_source->url2 = URL2;
merge_source->rev1 = rev1;
merge_source->rev2 = rev2;
APR_ARRAY_PUSH(merge_sources, merge_source_t *) = merge_source;
}
svn_pool_destroy(sesspool);
err = do_merge(NULL, NULL, merge_sources, target_abspath,
ancestral, related, same_repos,
ignore_ancestry, force, dry_run,
record_only, NULL, FALSE, FALSE, depth, merge_options,
&use_sleep, ctx, scratch_pool, scratch_pool);
if (use_sleep)
svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
if (err)
return svn_error_trace(err);
return SVN_NO_ERROR;
}
struct merge_baton_t {
const char *source1;
const svn_opt_revision_t *revision1;
const char *source2;
const svn_opt_revision_t *revision2;
const char *target_abspath;
svn_depth_t depth;
svn_boolean_t ignore_ancestry;
svn_boolean_t force;
svn_boolean_t record_only;
svn_boolean_t dry_run;
svn_boolean_t allow_mixed_rev;
const apr_array_header_t *merge_options;
svn_client_ctx_t *ctx;
};
static svn_error_t *
merge_cb(void *baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
struct merge_baton_t *b = baton;
SVN_ERR(merge_locked(b->source1, b->revision1, b->source2, b->revision2,
b->target_abspath, b->depth, b->ignore_ancestry,
b->force, b->record_only, b->dry_run,
b->allow_mixed_rev, b->merge_options, b->ctx,
scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
get_target_and_lock_abspath(const char **target_abspath,
const char **lock_abspath,
const char *target_wcpath,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
svn_node_kind_t kind;
SVN_ERR(svn_dirent_get_absolute(target_abspath, target_wcpath,
scratch_pool));
SVN_ERR(svn_wc_read_kind(&kind, ctx->wc_ctx, *target_abspath, FALSE,
scratch_pool));
if (kind == svn_node_dir)
*lock_abspath = *target_abspath;
else
*lock_abspath = svn_dirent_dirname(*target_abspath, scratch_pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_client_merge4(const char *source1,
const svn_opt_revision_t *revision1,
const char *source2,
const svn_opt_revision_t *revision2,
const char *target_wcpath,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
svn_boolean_t force,
svn_boolean_t record_only,
svn_boolean_t dry_run,
svn_boolean_t allow_mixed_rev,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *target_abspath, *lock_abspath;
struct merge_baton_t merge_baton;
SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
target_wcpath, ctx, pool));
merge_baton.source1 = source1;
merge_baton.revision1 = revision1;
merge_baton.source2 = source2;
merge_baton.revision2 = revision2;
merge_baton.target_abspath = target_abspath;
merge_baton.depth = depth;
merge_baton.ignore_ancestry = ignore_ancestry;
merge_baton.force = force;
merge_baton.record_only = record_only;
merge_baton.dry_run = dry_run;
merge_baton.allow_mixed_rev = allow_mixed_rev;
merge_baton.merge_options = merge_options;
merge_baton.ctx = ctx;
if (!dry_run)
SVN_ERR(svn_wc__call_with_write_lock(merge_cb, &merge_baton,
ctx->wc_ctx, lock_abspath, FALSE,
pool, pool));
else
SVN_ERR(merge_cb(&merge_baton, pool, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
mergeinfo_in_catalog(svn_boolean_t *in_catalog,
const char **cat_key_path,
const char *repos_rel_path,
svn_mergeinfo_t mergeinfo,
svn_revnum_t revision,
svn_mergeinfo_catalog_t catalog,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *walk_path = NULL;
*in_catalog = FALSE;
*cat_key_path = NULL;
if (mergeinfo && catalog && apr_hash_count(catalog))
{
const char *path = repos_rel_path;
svn_mergeinfo_t mergeinfo_in_cat = NULL;
while (1)
{
mergeinfo_in_cat = apr_hash_get(catalog, path, APR_HASH_KEY_STRING);
if (mergeinfo_in_cat)
{
*cat_key_path = apr_pstrdup(result_pool, path);
break;
}
else
{
walk_path = svn_relpath_join(svn_relpath_basename(path,
scratch_pool),
walk_path ? walk_path : "",
scratch_pool);
path = svn_relpath_dirname(path, scratch_pool);
if (path[0] == '\0')
break;
}
}
if (mergeinfo_in_cat)
{
if (walk_path)
SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(&mergeinfo_in_cat,
mergeinfo_in_cat,
walk_path,
scratch_pool,
scratch_pool));
SVN_ERR(svn_mergeinfo_intersect2(&mergeinfo_in_cat,
mergeinfo_in_cat, mergeinfo,
TRUE,
scratch_pool, scratch_pool));
SVN_ERR(svn_mergeinfo__equals(in_catalog, mergeinfo_in_cat,
mergeinfo, TRUE, scratch_pool));
}
}
return SVN_NO_ERROR;
}
typedef struct log_find_operative_baton_t
{
svn_mergeinfo_catalog_t merged_catalog;
svn_mergeinfo_catalog_t unmerged_catalog;
const char *target_abspath;
const char *source_repos_rel_path;
apr_pool_t *result_pool;
} log_find_operative_baton_t;
static svn_error_t *
log_find_operative_revs(void *baton,
svn_log_entry_t *log_entry,
apr_pool_t *pool)
{
log_find_operative_baton_t *log_baton = baton;
apr_hash_index_t *hi;
svn_revnum_t revision;
revision = log_entry->revision;
for (hi = apr_hash_first(pool, log_entry->changed_paths2);
hi;
hi = apr_hash_next(hi))
{
const char *subtree_missing_this_rev;
const char *path = svn__apr_hash_index_key(hi);
const char *rel_path;
const char *source_rel_path;
svn_boolean_t in_catalog;
svn_mergeinfo_t log_entry_as_mergeinfo;
rel_path = svn_fspath__skip_ancestor(log_baton->target_abspath, path);
if (rel_path == NULL)
continue;
source_rel_path = svn_relpath_join(log_baton->source_repos_rel_path,
rel_path, pool);
SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo,
apr_psprintf(pool, "%s:%ld",
path, revision),
pool));
SVN_ERR(mergeinfo_in_catalog(&in_catalog, &subtree_missing_this_rev,
source_rel_path, log_entry_as_mergeinfo,
revision, log_baton->merged_catalog,
pool, pool));
if (!in_catalog)
{
svn_mergeinfo_t unmerged_for_key;
const char *missing_path;
if (!subtree_missing_this_rev)
subtree_missing_this_rev = log_baton->source_repos_rel_path;
if (subtree_missing_this_rev
&& strcmp(subtree_missing_this_rev, source_rel_path))
{
const char *suffix =
svn_relpath_skip_ancestor(subtree_missing_this_rev,
source_rel_path);
missing_path = apr_pstrmemdup(pool, path,
strlen(path) - strlen(suffix) - 1);
}
else
{
missing_path = path;
}
SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo,
apr_psprintf(pool, "%s:%ld",
missing_path, revision),
log_baton->result_pool));
unmerged_for_key = apr_hash_get(log_baton->unmerged_catalog,
subtree_missing_this_rev,
APR_HASH_KEY_STRING);
if (unmerged_for_key)
{
SVN_ERR(svn_mergeinfo_merge(unmerged_for_key,
log_entry_as_mergeinfo,
log_baton->result_pool));
}
else
{
apr_hash_set(log_baton->unmerged_catalog,
apr_pstrdup(log_baton->result_pool,
subtree_missing_this_rev),
APR_HASH_KEY_STRING,
log_entry_as_mergeinfo);
}
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
find_unsynced_ranges(const char *source_repos_rel_path,
const char *target_repos_rel_path,
svn_mergeinfo_catalog_t unmerged_catalog,
svn_mergeinfo_catalog_t merged_catalog,
svn_mergeinfo_catalog_t true_unmerged_catalog,
svn_ra_session_t *ra_session,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_array_header_t *potentially_unmerged_ranges = NULL;
if (apr_hash_count(unmerged_catalog))
{
apr_hash_index_t *hi_catalog;
potentially_unmerged_ranges =
apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
for (hi_catalog = apr_hash_first(scratch_pool, unmerged_catalog);
hi_catalog;
hi_catalog = apr_hash_next(hi_catalog))
{
svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi_catalog);
apr_hash_index_t *hi_mergeinfo;
for (hi_mergeinfo = apr_hash_first(scratch_pool, mergeinfo);
hi_mergeinfo;
hi_mergeinfo = apr_hash_next(hi_mergeinfo))
{
apr_array_header_t *rangelist =
svn__apr_hash_index_val(hi_mergeinfo);
SVN_ERR(svn_rangelist_merge(&potentially_unmerged_ranges,
rangelist, scratch_pool));
}
}
}
if (potentially_unmerged_ranges)
{
svn_revnum_t oldest_rev =
(APR_ARRAY_IDX(potentially_unmerged_ranges,
0,
svn_merge_range_t *))->start + 1;
svn_revnum_t youngest_rev =
(APR_ARRAY_IDX(potentially_unmerged_ranges,
potentially_unmerged_ranges->nelts - 1,
svn_merge_range_t *))->end;
apr_array_header_t *log_targets = apr_array_make(scratch_pool, 1,
sizeof(const char *));
log_find_operative_baton_t log_baton;
log_baton.merged_catalog = merged_catalog;
log_baton.unmerged_catalog = true_unmerged_catalog;
log_baton.source_repos_rel_path = source_repos_rel_path;
log_baton.target_abspath = apr_psprintf(scratch_pool, "/%s",
target_repos_rel_path);
log_baton.result_pool = result_pool;
APR_ARRAY_PUSH(log_targets, const char *) = "";
SVN_ERR(svn_ra_get_log2(ra_session, log_targets, youngest_rev,
oldest_rev, 0, TRUE, FALSE, FALSE,
NULL, log_find_operative_revs, &log_baton,
scratch_pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
find_unmerged_mergeinfo(svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
svn_boolean_t *never_synched,
svn_revnum_t *youngest_merged_rev,
svn_revnum_t yc_ancestor_rev,
svn_mergeinfo_catalog_t source_catalog,
apr_hash_t *target_segments_hash,
const char *source_repos_rel_path,
const char *target_repos_rel_path,
svn_revnum_t target_rev,
svn_revnum_t source_rev,
svn_ra_session_t *source_ra_session,
svn_ra_session_t *target_ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
svn_mergeinfo_catalog_t new_catalog = apr_hash_make(result_pool);
svn_mergeinfo_t target_history_as_mergeinfo, source_history_as_mergeinfo;
svn_mergeinfo_t common_mergeinfo;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
svn_revnum_t old_rev, young_rev;
*never_synched = TRUE;
*youngest_merged_rev = SVN_INVALID_REVNUM;
for (hi = apr_hash_first(scratch_pool, target_segments_hash);
hi;
hi = apr_hash_next(hi))
{
const char *path = svn__apr_hash_index_key(hi);
apr_array_header_t *segments = svn__apr_hash_index_val(hi);
const char *source_path;
const char *source_path_rel_to_session;
svn_mergeinfo_t source_mergeinfo, filtered_mergeinfo;
svn_pool_clear(iterpool);
source_path_rel_to_session = path + strlen(target_repos_rel_path);
if (source_path_rel_to_session[0] == '/')
source_path_rel_to_session++;
source_path = svn_relpath_join(source_repos_rel_path,
source_path_rel_to_session, iterpool);
SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(
&target_history_as_mergeinfo, segments, iterpool));
SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
&target_history_as_mergeinfo, target_history_as_mergeinfo,
source_rev, yc_ancestor_rev, TRUE, iterpool, iterpool));
source_mergeinfo = apr_hash_get(source_catalog, source_path,
APR_HASH_KEY_STRING);
if (source_mergeinfo)
{
svn_mergeinfo_t explicit_source_target_history_intersection;
apr_hash_set(source_catalog, source_path, APR_HASH_KEY_STRING,
NULL);
SVN_ERR(svn_mergeinfo_intersect2(
&explicit_source_target_history_intersection,
source_mergeinfo, target_history_as_mergeinfo, TRUE,
iterpool, iterpool));
if (apr_hash_count(explicit_source_target_history_intersection))
{
*never_synched = FALSE;
SVN_ERR(svn_mergeinfo__get_range_endpoints(
&young_rev, &old_rev,
explicit_source_target_history_intersection,
iterpool));
if (!SVN_IS_VALID_REVNUM(*youngest_merged_rev)
|| (young_rev > *youngest_merged_rev))
*youngest_merged_rev = young_rev;
}
}
else
{
svn_node_kind_t kind;
svn_mergeinfo_catalog_t subtree_catalog;
apr_array_header_t *source_repos_rel_path_as_array;
SVN_ERR(svn_ra_check_path(source_ra_session,
source_path_rel_to_session,
source_rev, &kind, iterpool));
if (kind == svn_node_none)
continue;
source_repos_rel_path_as_array =
apr_array_make(iterpool, 1, sizeof(const char *));
APR_ARRAY_PUSH(source_repos_rel_path_as_array, const char *)
= source_path_rel_to_session;
SVN_ERR(svn_ra_get_mergeinfo(source_ra_session, &subtree_catalog,
source_repos_rel_path_as_array,
source_rev, svn_mergeinfo_inherited,
FALSE, iterpool));
if (subtree_catalog)
source_mergeinfo = apr_hash_get(subtree_catalog,
source_path_rel_to_session,
APR_HASH_KEY_STRING);
if (!source_mergeinfo)
source_mergeinfo = apr_hash_make(iterpool);
}
SVN_ERR(svn_client__repos_location_segments(&segments,
source_ra_session,
source_path_rel_to_session,
source_rev, source_rev,
SVN_INVALID_REVNUM,
ctx, iterpool));
SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(
&source_history_as_mergeinfo, segments, iterpool));
SVN_ERR(svn_mergeinfo_merge(source_mergeinfo,
source_history_as_mergeinfo, iterpool));
SVN_ERR(svn_mergeinfo_intersect2(&common_mergeinfo,
source_mergeinfo,
target_history_as_mergeinfo, TRUE,
iterpool, iterpool));
SVN_ERR(svn_mergeinfo_remove2(&filtered_mergeinfo,
common_mergeinfo,
target_history_as_mergeinfo, TRUE,
scratch_pool, iterpool));
apr_hash_set(new_catalog,
apr_pstrdup(scratch_pool, source_path),
APR_HASH_KEY_STRING,
filtered_mergeinfo);
}
if (apr_hash_count(source_catalog))
{
for (hi = apr_hash_first(scratch_pool, source_catalog);
hi;
hi = apr_hash_next(hi))
{
const char *source_path = svn__apr_hash_index_key(hi);
const char *source_path_rel_to_session =
svn_relpath_skip_ancestor(source_repos_rel_path, source_path);
svn_mergeinfo_t source_mergeinfo = svn__apr_hash_index_val(hi);
svn_mergeinfo_t filtered_mergeinfo;
const char *target_path;
apr_array_header_t *segments;
svn_error_t *err;
svn_pool_clear(iterpool);
target_path = source_path + strlen(source_repos_rel_path);
if (target_path[0] == '/')
target_path++;
err = svn_client__repos_location_segments(&segments,
target_ra_session,
target_path,
target_rev, target_rev,
SVN_INVALID_REVNUM,
ctx, iterpool);
if (err)
{
if (err->apr_err == SVN_ERR_FS_NOT_FOUND
|| err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
{
svn_error_clear(err);
err = NULL;
}
else
{
return svn_error_trace(err);
}
}
else
{
svn_mergeinfo_t explicit_source_target_history_intersection;
SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(
&target_history_as_mergeinfo, segments, iterpool));
SVN_ERR(svn_mergeinfo_intersect2(
&explicit_source_target_history_intersection,
source_mergeinfo, target_history_as_mergeinfo, TRUE,
iterpool, iterpool));
if (apr_hash_count(explicit_source_target_history_intersection))
{
*never_synched = FALSE;
SVN_ERR(svn_mergeinfo__get_range_endpoints(
&young_rev, &old_rev,
explicit_source_target_history_intersection, iterpool));
if (!SVN_IS_VALID_REVNUM(*youngest_merged_rev)
|| (young_rev > *youngest_merged_rev))
*youngest_merged_rev = young_rev;
}
SVN_ERR(svn_client__repos_location_segments(
&segments,
source_ra_session,
source_path_rel_to_session,
target_rev,
target_rev,
SVN_INVALID_REVNUM,
ctx, iterpool));
SVN_ERR(svn_mergeinfo__mergeinfo_from_segments(
&source_history_as_mergeinfo, segments, iterpool));
SVN_ERR(svn_mergeinfo_merge(source_mergeinfo,
source_history_as_mergeinfo,
iterpool));
SVN_ERR(svn_mergeinfo_intersect2(&common_mergeinfo,
source_mergeinfo,
target_history_as_mergeinfo,
TRUE, iterpool, iterpool));
SVN_ERR(svn_mergeinfo_remove2(&filtered_mergeinfo,
common_mergeinfo,
target_history_as_mergeinfo,
TRUE, scratch_pool, iterpool));
if (apr_hash_count(filtered_mergeinfo))
apr_hash_set(new_catalog,
apr_pstrdup(scratch_pool, source_path),
APR_HASH_KEY_STRING,
filtered_mergeinfo);
}
}
}
if (SVN_IS_VALID_REVNUM(*youngest_merged_rev))
SVN_ERR(svn_mergeinfo__filter_catalog_by_ranges(&new_catalog,
new_catalog,
*youngest_merged_rev,
0,
TRUE,
scratch_pool,
scratch_pool));
*unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(new_catalog,
result_pool);
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
calculate_left_hand_side(const char **url_left,
svn_revnum_t *rev_left,
svn_mergeinfo_t *merged_to_source_catalog,
svn_mergeinfo_t *unmerged_to_source_catalog,
const char *target_abspath,
const char *target_repos_rel_path,
apr_hash_t *subtrees_with_mergeinfo,
svn_revnum_t target_rev,
const char *source_repos_rel_path,
const char *source_repos_root,
const char *target_repos_root,
svn_revnum_t source_rev,
svn_ra_session_t *source_ra_session,
svn_ra_session_t *target_ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_array_header_t *segments;
svn_mergeinfo_catalog_t mergeinfo_catalog, unmerged_catalog;
apr_array_header_t *source_repos_rel_path_as_array
= apr_array_make(scratch_pool, 1, sizeof(const char *));
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_hash_index_t *hi;
apr_hash_t *segments_hash = apr_hash_make(scratch_pool);
svn_boolean_t never_synced;
svn_revnum_t youngest_merged_rev;
const char *yc_ancestor_path;
svn_revnum_t yc_ancestor_rev;
const char *source_url;
const char *target_url;
*url_left = NULL;
*rev_left = SVN_INVALID_REVNUM;
if (!apr_hash_get(subtrees_with_mergeinfo, target_abspath,
APR_HASH_KEY_STRING))
apr_hash_set(subtrees_with_mergeinfo, target_abspath,
APR_HASH_KEY_STRING, svn_string_create("", result_pool));
for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
hi;
hi = apr_hash_next(hi))
{
const char *absolute_path = svn__apr_hash_index_key(hi);
svn_string_t *mergeinfo_string = svn__apr_hash_index_val(hi);
const char *path_rel_to_session;
const char *path_rel_to_root;
svn_mergeinfo_t subtree_mergeinfo;
svn_error_t *err;
svn_pool_clear(iterpool);
err = svn_mergeinfo_parse(&subtree_mergeinfo, mergeinfo_string->data,
iterpool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
{
err = svn_error_createf(
SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
_("Invalid mergeinfo detected on '%s', "
"reintegrate merge not possible"),
svn_dirent_local_style(absolute_path, scratch_pool));
}
return svn_error_trace(err);
}
SVN_ERR(svn_client__path_relative_to_root(&path_rel_to_root,
ctx->wc_ctx, absolute_path,
target_repos_root, FALSE,
NULL, scratch_pool,
iterpool));
path_rel_to_session = svn_relpath_skip_ancestor(target_repos_rel_path,
path_rel_to_root);
SVN_ERR(svn_client__repos_location_segments(&segments,
target_ra_session,
path_rel_to_session,
target_rev, target_rev,
SVN_INVALID_REVNUM,
ctx, scratch_pool));
apr_hash_set(segments_hash,
apr_pstrdup(scratch_pool, path_rel_to_root),
APR_HASH_KEY_STRING, segments);
}
source_url = svn_path_url_add_component2(source_repos_root,
source_repos_rel_path,
iterpool);
target_url = svn_path_url_add_component2(source_repos_root,
target_repos_rel_path,
iterpool);
SVN_ERR(svn_client__get_youngest_common_ancestor(&yc_ancestor_path,
&yc_ancestor_rev,
source_url, source_rev,
target_url, target_rev,
ctx, iterpool));
if (!(yc_ancestor_path && SVN_IS_VALID_REVNUM(yc_ancestor_rev)))
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("'%s@%ld' must be ancestrally related to "
"'%s@%ld'"), source_url, source_rev,
target_url, target_rev);
if (source_rev == yc_ancestor_rev)
{
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
APR_ARRAY_PUSH(source_repos_rel_path_as_array, const char *) = "";
SVN_ERR(svn_ra_get_mergeinfo(source_ra_session, &mergeinfo_catalog,
source_repos_rel_path_as_array, source_rev,
svn_mergeinfo_inherited,
TRUE, iterpool));
if (mergeinfo_catalog)
SVN_ERR(svn_mergeinfo__add_prefix_to_catalog(&mergeinfo_catalog,
mergeinfo_catalog,
source_repos_rel_path,
iterpool, iterpool));
if (!mergeinfo_catalog)
mergeinfo_catalog = apr_hash_make(iterpool);
*merged_to_source_catalog = svn_mergeinfo_catalog_dup(mergeinfo_catalog,
result_pool);
SVN_ERR(find_unmerged_mergeinfo(&unmerged_catalog,
&never_synced,
&youngest_merged_rev,
yc_ancestor_rev,
mergeinfo_catalog,
segments_hash,
source_repos_rel_path,
target_repos_rel_path,
target_rev,
source_rev,
source_ra_session,
target_ra_session,
ctx,
iterpool, iterpool));
SVN_ERR(svn_client__elide_mergeinfo_catalog(unmerged_catalog,
iterpool));
*unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(unmerged_catalog,
result_pool);
if (never_synced)
{
*url_left = svn_path_url_add_component2(source_repos_root,
yc_ancestor_path, result_pool);
*rev_left = yc_ancestor_rev;
}
else
{
svn_opt_revision_t peg_revision, youngest_rev, unspecified_rev;
svn_opt_revision_t *start_revision;
const char *youngest_url;
peg_revision.kind = svn_opt_revision_number;
peg_revision.value.number = target_rev;
youngest_rev.kind = svn_opt_revision_number;
youngest_rev.value.number = youngest_merged_rev;
unspecified_rev.kind = svn_opt_revision_unspecified;
*rev_left = youngest_rev.value.number;
SVN_ERR(svn_client__repos_locations(&youngest_url, &start_revision,
NULL, NULL, target_ra_session,
target_url, &peg_revision,
&youngest_rev, &unspecified_rev,
ctx, iterpool));
*url_left = apr_pstrdup(result_pool, youngest_url);
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
merge_reintegrate_locked(const char *source,
const svn_opt_revision_t *peg_revision,
const char *target_abspath,
svn_boolean_t dry_run,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
const char *wc_repos_root, *source_repos_root;
svn_opt_revision_t working_revision;
svn_ra_session_t *target_ra_session;
svn_ra_session_t *source_ra_session;
const char *source_repos_rel_path, *target_repos_rel_path;
const char *yc_ancestor_path;
svn_revnum_t yc_ancestor_rev;
const char *url1, *url2;
svn_revnum_t rev1, rev2;
svn_mergeinfo_t unmerged_to_source_mergeinfo_catalog;
svn_mergeinfo_t final_unmerged_catalog;
svn_mergeinfo_t merged_to_source_mergeinfo_catalog;
svn_boolean_t use_sleep = FALSE;
svn_error_t *err;
apr_hash_t *subtrees_with_mergeinfo;
const char *target_url;
svn_revnum_t target_base_rev;
svn_node_kind_t kind;
svn_opt_revision_t no_rev;
SVN_ERR(svn_io_check_path(target_abspath, &kind, scratch_pool));
if (kind == svn_node_none)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("Path '%s' does not exist"),
svn_dirent_local_style(target_abspath,
scratch_pool));
SVN_ERR(svn_client_url_from_path2(&url2, source, ctx,
scratch_pool, scratch_pool));
if (! url2)
return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("'%s' has no URL"),
svn_dirent_local_style(source, scratch_pool));
SVN_ERR(svn_client__get_repos_root(&wc_repos_root, target_abspath,
ctx, scratch_pool, scratch_pool));
SVN_ERR(svn_client__get_repos_root(&source_repos_root, url2,
ctx, scratch_pool, scratch_pool));
if (strcmp(source_repos_root, wc_repos_root) != 0)
return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
_("'%s' must be from the same repository as "
"'%s'"), svn_dirent_local_style(source,
scratch_pool),
svn_dirent_local_style(target_abspath,
scratch_pool));
SVN_ERR(ensure_wc_is_suitable_merge_target(target_abspath, ctx,
FALSE, FALSE, FALSE,
scratch_pool));
SVN_ERR(svn_wc__node_get_base_rev(&target_base_rev, ctx->wc_ctx,
target_abspath, scratch_pool));
rev1 = target_base_rev;
source_repos_rel_path = svn_uri_skip_ancestor(wc_repos_root, url2,
scratch_pool);
SVN_ERR(svn_client__path_relative_to_root(&target_repos_rel_path,
ctx->wc_ctx, target_abspath,
wc_repos_root, FALSE, NULL,
scratch_pool, scratch_pool));
if (svn_path_is_empty(source_repos_rel_path)
|| svn_path_is_empty(target_repos_rel_path))
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("Neither the reintegrate source nor target "
"can be the root of the repository"));
working_revision.kind = svn_opt_revision_working;
SVN_ERR(svn_client_propget4(&subtrees_with_mergeinfo, SVN_PROP_MERGEINFO,
target_abspath, &working_revision,
&working_revision, NULL, svn_depth_infinity,
NULL, ctx, scratch_pool, scratch_pool));
if (apr_hash_count(subtrees_with_mergeinfo))
{
apr_hash_t *externals;
apr_hash_index_t *hi;
SVN_ERR(svn_wc__externals_defined_below(&externals, ctx->wc_ctx,
target_abspath, scratch_pool,
scratch_pool));
for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
hi;
hi = apr_hash_next(hi))
{
const char *wc_path = svn__apr_hash_index_key(hi);
if (apr_hash_get(externals, wc_path, APR_HASH_KEY_STRING))
{
apr_hash_set(subtrees_with_mergeinfo, wc_path,
APR_HASH_KEY_STRING, NULL);
continue;
}
}
}
no_rev.kind = svn_opt_revision_unspecified;
SVN_ERR(svn_client__ra_session_from_path(&source_ra_session, &rev2, &url2,
url2, NULL, peg_revision, &no_rev,
ctx, scratch_pool));
SVN_ERR(svn_wc__node_get_url(&target_url, ctx->wc_ctx, target_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_client__open_ra_session_internal(&target_ra_session, NULL,
target_url,
NULL, NULL, FALSE, FALSE,
ctx, scratch_pool));
SVN_ERR(calculate_left_hand_side(&url1, &rev1,
&merged_to_source_mergeinfo_catalog,
&unmerged_to_source_mergeinfo_catalog,
target_abspath,
target_repos_rel_path,
subtrees_with_mergeinfo,
rev1,
source_repos_rel_path,
source_repos_root,
wc_repos_root,
rev2,
source_ra_session,
target_ra_session,
ctx,
scratch_pool, scratch_pool));
if (! url1)
return SVN_NO_ERROR;
if (strcmp(url1, target_url))
SVN_ERR(svn_ra_reparent(target_ra_session, url1, scratch_pool));
SVN_ERR(svn_client__get_youngest_common_ancestor(&yc_ancestor_path,
&yc_ancestor_rev,
url2, rev2,
url1, rev1,
ctx, scratch_pool));
if (!(yc_ancestor_path && SVN_IS_VALID_REVNUM(yc_ancestor_rev)))
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("'%s@%ld' must be ancestrally related to "
"'%s@%ld'"), url1, rev1, url2, rev2);
if (rev1 > yc_ancestor_rev)
{
final_unmerged_catalog = apr_hash_make(scratch_pool);
SVN_ERR(find_unsynced_ranges(source_repos_rel_path,
yc_ancestor_path,
unmerged_to_source_mergeinfo_catalog,
merged_to_source_mergeinfo_catalog,
final_unmerged_catalog,
target_ra_session, scratch_pool,
scratch_pool));
if (apr_hash_count(final_unmerged_catalog))
{
svn_string_t *source_mergeinfo_cat_string;
SVN_ERR(svn_mergeinfo__catalog_to_formatted_string(
&source_mergeinfo_cat_string,
final_unmerged_catalog,
" ", " Missing ranges: ", scratch_pool));
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
NULL,
_("Reintegrate can only be used if "
"revisions %ld through %ld were "
"previously merged from %s to the "
"reintegrate source, but this is "
"not the case:\n%s"),
yc_ancestor_rev + 1, rev2,
target_url,
source_mergeinfo_cat_string->data);
}
}
err = merge_cousins_and_supplement_mergeinfo(target_abspath,
target_ra_session,
source_ra_session,
url1, rev1, url2, rev2,
yc_ancestor_rev,
source_repos_root,
wc_repos_root,
svn_depth_infinity,
FALSE, FALSE, FALSE, dry_run,
merge_options, &use_sleep,
ctx, scratch_pool);
if (use_sleep)
svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
if (err)
return svn_error_trace(err);
return SVN_NO_ERROR;
}
struct merge_reintegrate_baton_t {
const char *source;
const svn_opt_revision_t *peg_revision;
const char *target_abspath;
svn_boolean_t dry_run;
const apr_array_header_t *merge_options;
svn_client_ctx_t *ctx;
};
static svn_error_t *
merge_reintegrate_cb(void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
struct merge_reintegrate_baton_t *b = baton;
SVN_ERR(merge_reintegrate_locked(b->source, b->peg_revision,
b->target_abspath, b->dry_run,
b->merge_options, b->ctx,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_client_merge_reintegrate(const char *source,
const svn_opt_revision_t *peg_revision,
const char *target_wcpath,
svn_boolean_t dry_run,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *target_abspath, *lock_abspath;
struct merge_reintegrate_baton_t merge_reintegrate_baton;
SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
target_wcpath, ctx, pool));
merge_reintegrate_baton.source = source;
merge_reintegrate_baton.peg_revision = peg_revision;
merge_reintegrate_baton.target_abspath = target_abspath;
merge_reintegrate_baton.dry_run = dry_run;
merge_reintegrate_baton.merge_options = merge_options;
merge_reintegrate_baton.ctx = ctx;
if (!dry_run)
SVN_ERR(svn_wc__call_with_write_lock(merge_reintegrate_cb,
&merge_reintegrate_baton,
ctx->wc_ctx, lock_abspath, FALSE,
pool, pool));
else
SVN_ERR(merge_reintegrate_cb(&merge_reintegrate_baton, pool, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
merge_peg_locked(const char *source,
const apr_array_header_t *ranges_to_merge,
const svn_opt_revision_t *peg_revision,
const char *target_abspath,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
svn_boolean_t force,
svn_boolean_t record_only,
svn_boolean_t dry_run,
svn_boolean_t allow_mixed_rev,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
const char *URL;
apr_array_header_t *merge_sources;
const char *wc_repos_root, *source_repos_root;
svn_ra_session_t *ra_session;
apr_pool_t *sesspool;
svn_boolean_t use_sleep = FALSE;
svn_error_t *err;
svn_boolean_t same_repos;
svn_node_kind_t target_kind;
SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
SVN_ERR(svn_io_check_path(target_abspath, &target_kind, scratch_pool));
if (target_kind == svn_node_none)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("Path '%s' does not exist"),
svn_dirent_local_style(target_abspath,
scratch_pool));
SVN_ERR(svn_client_url_from_path2(&URL, source, ctx,
scratch_pool, scratch_pool));
if (! URL)
return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("'%s' has no URL"),
svn_dirent_local_style(source, scratch_pool));
SVN_ERR(svn_wc_read_kind(&target_kind, ctx->wc_ctx, target_abspath, FALSE,
scratch_pool));
if (target_kind != svn_node_dir && target_kind != svn_node_file)
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("Merge target '%s' does not exist in the "
"working copy"), target_abspath);
SVN_ERR(ensure_wc_is_suitable_merge_target(target_abspath, ctx,
allow_mixed_rev, TRUE, TRUE,
scratch_pool));
SVN_ERR(svn_client__get_repos_root(&wc_repos_root, target_abspath,
ctx, scratch_pool, scratch_pool));
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_ra_get_repos_root2(ra_session, &source_repos_root, scratch_pool));
SVN_ERR(normalize_merge_sources(&merge_sources, source, URL,
source_repos_root, peg_revision,
ranges_to_merge, ra_session, ctx,
scratch_pool, scratch_pool));
if (strcmp(wc_repos_root, source_repos_root) != 0)
{
const char *source_repos_uuid;
const char *wc_repos_uuid;
SVN_ERR(svn_ra_get_uuid2(ra_session, &source_repos_uuid, scratch_pool));
SVN_ERR(svn_client_uuid_from_path2(&wc_repos_uuid, target_abspath,
ctx, scratch_pool, scratch_pool));
same_repos = (strcmp(wc_repos_uuid, source_repos_uuid) == 0);
}
else
same_repos = TRUE;
svn_pool_destroy(sesspool);
err = do_merge(NULL, NULL, merge_sources, target_abspath,
TRUE, TRUE, same_repos, ignore_ancestry, force, dry_run,
record_only, NULL, FALSE, FALSE, depth, merge_options,
&use_sleep, ctx, scratch_pool, scratch_pool);
if (use_sleep)
svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
if (err)
return svn_error_trace(err);
return SVN_NO_ERROR;
}
struct merge_peg_baton_t {
const char *source;
const apr_array_header_t *ranges_to_merge;
const svn_opt_revision_t *peg_revision;
const char *target_abspath;
svn_depth_t depth;
svn_boolean_t ignore_ancestry;
svn_boolean_t force;
svn_boolean_t record_only;
svn_boolean_t dry_run;
svn_boolean_t allow_mixed_rev;
const apr_array_header_t *merge_options;
svn_client_ctx_t *ctx;
};
static svn_error_t *
merge_peg_cb(void *baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool)
{
struct merge_peg_baton_t *b = baton;
SVN_ERR(merge_peg_locked(b->source, b->ranges_to_merge, b->peg_revision,
b->target_abspath, b->depth, b->ignore_ancestry,
b->force, b->record_only, b->dry_run,
b->allow_mixed_rev, b->merge_options, b->ctx,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_client_merge_peg4(const char *source,
const apr_array_header_t *ranges_to_merge,
const svn_opt_revision_t *peg_revision,
const char *target_wcpath,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
svn_boolean_t force,
svn_boolean_t record_only,
svn_boolean_t dry_run,
svn_boolean_t allow_mixed_rev,
const apr_array_header_t *merge_options,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *target_abspath, *lock_abspath;
struct merge_peg_baton_t merge_peg_baton;
if (ranges_to_merge->nelts == 0)
return SVN_NO_ERROR;
SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
target_wcpath, ctx, pool));
merge_peg_baton.source = source;
merge_peg_baton.ranges_to_merge = ranges_to_merge;
merge_peg_baton.peg_revision = peg_revision;
merge_peg_baton.target_abspath = target_abspath;
merge_peg_baton.depth = depth;
merge_peg_baton.ignore_ancestry = ignore_ancestry;
merge_peg_baton.force = force;
merge_peg_baton.record_only = record_only;
merge_peg_baton.dry_run = dry_run;
merge_peg_baton.allow_mixed_rev = allow_mixed_rev;
merge_peg_baton.merge_options = merge_options;
merge_peg_baton.ctx = ctx;
if (!dry_run)
SVN_ERR(svn_wc__call_with_write_lock(merge_peg_cb, &merge_peg_baton,
ctx->wc_ctx, lock_abspath, FALSE,
pool, pool));
else
SVN_ERR(merge_peg_cb(&merge_peg_baton, pool, pool));
return SVN_NO_ERROR;
}