Alien-SVN
view release on metacpan or search on metacpan
src/subversion/subversion/libsvn_client/merge.c view on Meta::CPAN
} /* Iteration over each merge source in younger_mergeinfo. */
} /* if (younger_mergeinfo) */
/* Filter self-referential mergeinfo from "older" mergeinfo. */
if (mergeinfo)
{
svn_mergeinfo_t implicit_mergeinfo;
SVN_ERR(svn_client__get_history_as_mergeinfo(
&implicit_mergeinfo, NULL,
&target_base, target_base.rev, SVN_INVALID_REVNUM,
ra_session, ctx, iterpool));
/* Remove PATH's implicit mergeinfo from the incoming mergeinfo. */
SVN_ERR(svn_mergeinfo_remove2(&filtered_mergeinfo,
implicit_mergeinfo,
mergeinfo, TRUE, iterpool, iterpool));
}
/* Combine whatever older and younger filtered mergeinfo exists
into filtered_mergeinfo. */
if (filtered_mergeinfo && filtered_younger_mergeinfo)
SVN_ERR(svn_mergeinfo_merge2(filtered_mergeinfo,
filtered_younger_mergeinfo, iterpool,
iterpool));
else if (filtered_younger_mergeinfo)
filtered_mergeinfo = filtered_younger_mergeinfo;
/* If there is any incoming mergeinfo remaining after filtering
then put it in adjusted_props. */
if (filtered_mergeinfo && apr_hash_count(filtered_mergeinfo))
{
/* Convert filtered_mergeinfo to a svn_prop_t and put it
back in the array. */
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;
}
/* Prepare a set of property changes PROPCHANGES to be used for a merge
operation on LOCAL_ABSPATH.
Remove all non-regular prop-changes (entry-props and WC-props).
Remove all non-mergeinfo prop-changes if it's a record-only merge.
Remove self-referential mergeinfo (### in some cases...)
Remove foreign-repository mergeinfo (### in some cases...)
Store the resulting property changes in *PROP_UPDATES.
Store information on where mergeinfo is updated in MERGE_B.
Used for both file and directory property merges. */
static svn_error_t *
prepare_merge_props_changed(const apr_array_header_t **prop_updates,
const char *local_abspath,
const apr_array_header_t *propchanges,
merge_cmd_baton_t *merge_b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_array_header_t *props;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
/* We only want to merge "regular" version properties: by
definition, 'svn merge' shouldn't touch any data within .svn/ */
SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
result_pool));
/* If we are only applying mergeinfo changes then we need to do
additional filtering of PROPS so it contains only mergeinfo changes. */
if (merge_b->record_only && props->nelts)
{
apr_array_header_t *mergeinfo_props =
apr_array_make(result_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)
{
/* Issue #3383: We don't want mergeinfo from a foreign repos.
If this is a merge from a foreign repository we must strip all
incoming mergeinfo (including mergeinfo deletions). */
if (! merge_b->same_repos)
SVN_ERR(omit_mergeinfo_changes(&props, props, result_pool));
/* If this is a forward merge then don't add new mergeinfo to
PATH that is already part of PATH's own history, see
http://svn.haxx.se/dev/archive-2008-09/0006.shtml. If the
merge sources are not ancestral then there is no concept of a
'forward' or 'reverse' merge and we filter unconditionally. */
if (merge_b->merge_source.loc1->rev < merge_b->merge_source.loc2->rev
|| !merge_b->merge_source.ancestral)
{
if (HONOR_MERGEINFO(merge_b) || merge_b->reintegrate_merge)
SVN_ERR(filter_self_referential_mergeinfo(&props,
local_abspath,
src/subversion/subversion/libsvn_client/merge.c view on Meta::CPAN
}
if (merge_b->same_repos)
{
const char *original_url;
original_url = svn_path_url_add_component2(
merge_b->merge_source.loc2->url,
relpath, scratch_pool);
/* Limitation (aka HACK):
We create a newly added directory with an original URL and
revision as that in the repository, but without its properties
and children.
When the merge is cancelled before the final dir_added(), the
copy won't really represent the in-repository state of the node.
*/
SVN_ERR(svn_wc_add4(merge_b->ctx->wc_ctx, local_abspath,
svn_depth_infinity,
original_url,
right_source->revision,
merge_b->ctx->cancel_func,
merge_b->ctx->cancel_baton,
NULL, NULL /* no notify! */,
scratch_pool));
}
else
{
SVN_ERR(svn_wc_add_from_disk2(merge_b->ctx->wc_ctx, local_abspath,
apr_hash_make(scratch_pool),
NULL, NULL /* no notify! */,
scratch_pool));
}
if (old_tc != NULL)
{
/* ### Should be atomic with svn_wc_add(4|_from_disk2)() */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
svn_node_dir,
db->tree_conflict_action,
db->tree_conflict_reason,
old_tc, FALSE,
scratch_pool));
}
}
if (! db->shadowed && !merge_b->record_only)
SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_dir,
db->add_is_replace, scratch_pool));
}
return SVN_NO_ERROR;
}
/* An svn_diff_tree_processor_t function.
*
* Called after merge_dir_opened() when a node exists in both the left and
* right source, but has its properties changed inbetween.
*
* After the merge_dir_opened() but before the call to this merge_dir_changed()
* function all descendants will have been updated.
*/
static svn_error_t *
merge_dir_changed(const char *relpath,
const svn_diff_source_t *left_source,
const svn_diff_source_t *right_source,
/*const*/ apr_hash_t *left_props,
/*const*/ apr_hash_t *right_props,
const apr_array_header_t *prop_changes,
void *dir_baton,
const struct svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
merge_cmd_baton_t *merge_b = processor->baton;
struct merge_dir_baton_t *db = dir_baton;
const apr_array_header_t *props;
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
relpath, scratch_pool);
SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
if (db->shadowed)
{
if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
{
/* We haven't notified for this node yet: report a skip */
SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
svn_wc_notify_update_shadowed_update,
db->skip_reason, scratch_pool));
}
return SVN_NO_ERROR;
}
SVN_ERR(prepare_merge_props_changed(&props, local_abspath, prop_changes,
merge_b, scratch_pool, scratch_pool));
if (props->nelts)
{
const svn_wc_conflict_version_t *left;
const svn_wc_conflict_version_t *right;
svn_client_ctx_t *ctx = merge_b->ctx;
svn_wc_notify_state_t prop_state;
SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
svn_node_dir, &merge_b->merge_source,
merge_b->target,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc_merge_props3(&prop_state, ctx->wc_ctx, local_abspath,
left, right,
left_props, props,
merge_b->dry_run,
NULL, NULL,
ctx->cancel_func, ctx->cancel_baton,
scratch_pool));
if (prop_state == svn_wc_notify_state_conflicted)
{
src/subversion/subversion/libsvn_client/merge.c view on Meta::CPAN
else
{
void *file_baton = NULL;
svn_boolean_t skip = FALSE;
apr_array_header_t *propchanges;
/* Deduce property diffs. */
SVN_ERR(svn_prop_diffs(&propchanges, right_props, left_props,
iterpool));
SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
left_source,
right_source,
NULL /* copyfrom_source */,
NULL /* dir_baton */,
processor,
iterpool, iterpool));
if (! skip)
SVN_ERR(processor->file_changed(target_relpath,
left_source,
right_source,
left_file,
right_file,
left_props,
right_props,
TRUE /* file changed */,
propchanges,
file_baton,
processor,
iterpool));
}
if (is_path_conflicted_by_merge(merge_b))
{
merge_source_t *remaining_range = NULL;
if (real_source->loc2->rev != source->loc2->rev)
remaining_range = subrange_source(source,
real_source->loc2->rev,
source->loc2->rev,
scratch_pool);
*conflict_report = single_range_conflict_report_create(
real_source, remaining_range, result_pool);
/* Only record partial mergeinfo if only a partial merge was
performed before a conflict was encountered. */
range.end = r->end;
break;
}
/* Now delete the just merged range from the hash
(This list is used from notify_merge_begin)
Directory merges use remove_first_range_from_remaining_ranges() */
svn_sort__array_delete(ranges_to_merge, 0, 1);
}
merge_b->notify_begin.last_abspath = NULL;
} /* !merge_b->record_only */
/* Record updated WC mergeinfo to account for our new merges, minus
any unresolved conflicts and skips. We use the original
REMAINING_RANGES here because we want to record all the requested
merge ranges, include the noop ones. */
if (RECORD_MERGEINFO(merge_b) && remaining_ranges->nelts)
{
const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
scratch_pool);
svn_rangelist_t *filtered_rangelist;
/* Filter any ranges from TARGET_WCPATH's own history, there is no
need to record this explicitly in mergeinfo, it is already part
of TARGET_WCPATH's natural history (implicit mergeinfo). */
SVN_ERR(filter_natural_history_from_mergeinfo(
&filtered_rangelist,
mergeinfo_path,
merge_target->implicit_mergeinfo,
&range,
iterpool));
/* Only record mergeinfo if there is something other than
self-referential mergeinfo, but don't record mergeinfo if
TARGET_WCPATH was skipped. */
if (filtered_rangelist->nelts
&& (apr_hash_count(merge_b->skipped_abspaths) == 0))
{
apr_hash_t *merges = apr_hash_make(iterpool);
/* If merge target has inherited mergeinfo set it before
recording the first merge range. */
if (inherited)
SVN_ERR(svn_client__record_wc_mergeinfo(target_abspath,
target_mergeinfo,
FALSE, ctx,
iterpool));
svn_hash_sets(merges, target_abspath, filtered_rangelist);
if (!squelch_mergeinfo_notifications)
{
/* Notify that we are recording mergeinfo describing a merge. */
svn_merge_range_t n_range;
SVN_ERR(svn_mergeinfo__get_range_endpoints(
&n_range.end, &n_range.start, merges, iterpool));
n_range.inheritable = TRUE;
notify_mergeinfo_recording(target_abspath, &n_range,
merge_b->ctx, iterpool);
}
SVN_ERR(update_wc_mergeinfo(result_catalog, target_abspath,
mergeinfo_path, merges, is_rollback,
ctx, iterpool));
}
}
merge_b->notify_begin.nodes_with_mergeinfo = NULL;
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
src/subversion/subversion/libsvn_client/merge.c view on Meta::CPAN
svn_error_t *err;
svn_mergeinfo_t subtree_history_as_mergeinfo;
svn_rangelist_t *child_merge_src_rangelist;
svn_client__pathrev_t *subtree_mergeinfo_pathrev
= svn_client__pathrev_create_with_relpath(
merge_b->target->loc.repos_root_url,
merge_b->target->loc.repos_uuid,
merged_range->end, child_merge_src_fspath + 1,
iterpool);
/* Confirm that the naive mergeinfo we want to set on
CHILD->ABSPATH both exists and is part of
(MERGE_SOURCE_PATH+CHILD_REPOS_PATH)@MERGED_RANGE->END's
history. */
/* We know MERGED_RANGE->END is younger than MERGE_RANGE->START
because we only do this for forward merges. */
err = svn_client__get_history_as_mergeinfo(
&subtree_history_as_mergeinfo, NULL,
subtree_mergeinfo_pathrev,
merged_range->end, merged_range->start,
merge_b->ra_session2, merge_b->ctx, iterpool);
/* If CHILD is a subtree it may have been deleted prior to
MERGED_RANGE->END so the above call to get its history
will fail. */
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 = svn_hash_gets(
subtree_history_as_mergeinfo,
child_merge_src_fspath);
SVN_ERR(svn_rangelist_intersect(&child_merge_rangelist,
child_merge_rangelist,
child_merge_src_rangelist,
FALSE, iterpool));
if (child->record_noninheritable)
svn_rangelist__set_inheritance(child_merge_rangelist,
FALSE);
}
}
svn_hash_sets(child_merges, child->abspath, child_merge_rangelist);
SVN_ERR(update_wc_mergeinfo(result_catalog,
child->abspath,
child_merge_src_fspath,
child_merges, is_rollback,
merge_b->ctx, iterpool));
/* Once is enough: We don't need to record mergeinfo describing
the merge a second. If CHILD->ABSPATH is in
MERGE_B->ADDED_ABSPATHS, we'll do just that, so remove the
former from the latter. */
svn_hash_sets(merge_b->added_abspaths, child->abspath, NULL);
}
/* Elide explicit subtree mergeinfo whether or not we updated it. */
if (i > 0)
{
svn_boolean_t in_switched_subtree = FALSE;
if (child->switched)
in_switched_subtree = TRUE;
else if (i > 1)
{
/* Check if CHILD is part of a switched subtree */
svn_client__merge_path_t *parent;
int j = i - 1;
for (; j > 0; j--)
{
parent = APR_ARRAY_IDX(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;
}
}
}
/* Allow mergeinfo on switched subtrees to elide to the
repository. Otherwise limit elision to the merge target
for now. do_directory_merge() will eventually try to
elide that when the merge is complete. */
SVN_ERR(svn_client__elide_mergeinfo(
child->abspath,
in_switched_subtree ? NULL : merge_b->target->abspath,
merge_b->ctx, iterpool));
}
} /* (i = 0; i < notify_b->children_with_mergeinfo->nelts; i++) */
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Helper for do_directory_merge().
Record mergeinfo describing a merge of
MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
MERGEINFO_FSPATH to each path in ADDED_ABSPATHS which has explicit
mergeinfo or is the immediate child of a parent with explicit
non-inheritable mergeinfo.
DEPTH, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS, are
cascaded from do_directory_merge's arguments of the same names.
Note: This is intended to support forward merges only, i.e.
MERGED_RANGE->START must be older than MERGED_RANGE->END.
*/
static svn_error_t *
record_mergeinfo_for_added_subtrees(
svn_merge_range_t *merged_range,
const char *mergeinfo_fspath,
svn_depth_t depth,
src/subversion/subversion/libsvn_client/merge.c view on Meta::CPAN
/* Simplify unmerged_catalog through elision then make a copy in POOL. */
SVN_ERR(svn_client__elide_mergeinfo_catalog(unmerged_catalog,
iterpool));
*unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(unmerged_catalog,
result_pool);
if (youngest_merged_rev == SVN_INVALID_REVNUM)
{
/* We never merged to the source. Just return the branch point. */
*left_p = svn_client__pathrev_dup(yc_ancestor, result_pool);
}
else
{
/* We've previously merged some or all of the target, up to
youngest_merged_rev, to the source. Set
*LEFT_P to cover the youngest part of this range. */
SVN_ERR(svn_client__repos_location(left_p, target_ra_session,
&target->loc, youngest_merged_rev,
ctx, result_pool, iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
/* Determine the URLs and revisions needed to perform a reintegrate merge
* from SOURCE_LOC into the working copy at TARGET.
*
* SOURCE_RA_SESSION and TARGET_RA_SESSION are RA sessions opened to the
* URLs of SOURCE_LOC and TARGET->loc respectively.
*
* Set *SOURCE_P to
* the source-left and source-right locations of the required merge. Set
* *YC_ANCESTOR_P to the location of the youngest ancestor.
* Any of these output pointers may be NULL if not wanted.
*
* See svn_client_find_reintegrate_merge() for other details.
*/
static svn_error_t *
find_reintegrate_merge(merge_source_t **source_p,
svn_client__pathrev_t **yc_ancestor_p,
svn_ra_session_t *source_ra_session,
const svn_client__pathrev_t *source_loc,
svn_ra_session_t *target_ra_session,
const merge_target_t *target,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_client__pathrev_t *yc_ancestor;
svn_client__pathrev_t *loc1;
merge_source_t source;
svn_mergeinfo_catalog_t unmerged_to_source_mergeinfo_catalog;
svn_mergeinfo_catalog_t merged_to_source_mergeinfo_catalog;
svn_error_t *err;
apr_hash_t *subtrees_with_mergeinfo;
assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
/* As the WC tree is "pure", use its last-updated-to revision as
the default revision for the left side of our merge, since that's
what the repository sub-tree is required to be up to date with
(with regard to the WC). */
/* ### Bogus/obsolete comment? */
/* Can't reintegrate to or from the root of the repository. */
if (strcmp(source_loc->url, source_loc->repos_root_url) == 0
|| strcmp(target->loc.url, target->loc.repos_root_url) == 0)
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"));
/* Find all the subtrees in TARGET_WCPATH that have explicit mergeinfo. */
err = get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
target->abspath, svn_depth_infinity,
ctx, scratch_pool, scratch_pool);
/* Issue #3896: If invalid mergeinfo in the reintegrate target
prevents us from proceeding, then raise the best error possible. */
if (err && err->apr_err == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
err = svn_error_quick_wrap(err, _("Reintegrate merge not possible"));
SVN_ERR(err);
SVN_ERR(calculate_left_hand_side(&loc1,
&merged_to_source_mergeinfo_catalog,
&unmerged_to_source_mergeinfo_catalog,
target,
subtrees_with_mergeinfo,
source_loc,
source_ra_session,
target_ra_session,
ctx,
scratch_pool, scratch_pool));
/* Did calculate_left_hand_side() decide that there was no merge to
be performed here? */
if (! loc1)
{
if (source_p)
*source_p = NULL;
if (yc_ancestor_p)
*yc_ancestor_p = NULL;
return SVN_NO_ERROR;
}
source.loc1 = loc1;
source.loc2 = source_loc;
/* If the target was moved after the source was branched from it,
it is possible that the left URL differs from the target's current
URL. If so, then adjust TARGET_RA_SESSION to point to the old URL. */
if (strcmp(source.loc1->url, target->loc.url))
SVN_ERR(svn_ra_reparent(target_ra_session, source.loc1->url, scratch_pool));
SVN_ERR(svn_client__get_youngest_common_ancestor(
&yc_ancestor, source.loc2, source.loc1, target_ra_session,
ctx, scratch_pool, scratch_pool));
if (! yc_ancestor)
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
_("'%s@%ld' must be ancestrally related to "
( run in 0.440 second using v1.01-cache-2.11-cpan-5623c5533a1 )